This is page 1 of 2. Use http://codebase.md/illuminaresolutions/n8n-mcp-server?page={x} to view the full context. # Directory Structure ``` ├── .gitignore ├── DEVELOPMENT.md ├── LICENSE ├── LLM_GUIDE.md ├── N8N_API.yml ├── package-lock.json ├── package.json ├── README.md ├── smithery.yml ├── src │ └── index.ts ├── tsconfig.json └── TYPEKIT_MCP_SDK.md ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Build build/ dist/ *.tsbuildinfo # Dependencies node_modules/ .pnp/ .pnp.js .npm/ # Environment .env .env.local .env.*.local .env.development.local .env.test.local .env.production.local # Logs logs/ *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # IDE .idea/ .vscode/ *.swp *.swo .project .classpath .settings/ *.sublime-workspace *.sublime-project # OS .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db Desktop.ini # Testing coverage/ .nyc_output/ junit.xml *.lcov # Temporary files *.tmp *.temp .cache/ .temp/ tmp/ # TypeScript *.js.map ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # n8n MCP Server An MCP server that provides access to n8n workflows, executions, credentials, and more through the Model Context Protocol. This allows Large Language Models (LLMs) to interact with n8n instances in a secure and standardized way. ## Installation ### Get your n8n API Key 1. Log into your n8n instance 2. Click your user icon in the bottom left 3. Go to Settings 4. Select API 5. Click "Create API Key" 6. Copy your API key (you won't be able to see it again) ### Install the MCP Server #### Option 1: Install from npm (Recommended) ```bash npm install -g @illuminaresolutions/n8n-mcp-server ``` #### Option 2: Install from Source 1. Clone the repository: ```bash git clone https://github.com/illuminaresolutions/n8n-mcp-server.git cd n8n-mcp-server ``` 2. Install dependencies and build: ```bash npm install npm run build ``` 3. Start the server in the background: ```bash nohup npm start > n8n-mcp.log 2>&1 & ``` To stop the server: ```bash pkill -f "node build/index.js" ``` Note: When installing from npm, the server will be available as `n8n-mcp-server` in your PATH. ## Configuration ### Claude Desktop 1. Open your Claude Desktop configuration: ``` ~/Library/Application Support/Claude/claude_desktop_config.json ``` 2. Add the n8n configuration: ```json { "mcpServers": { "n8n": { "command": "n8n-mcp-server", "env": { "N8N_HOST": "https://your-n8n-instance.com", "N8N_API_KEY": "your-api-key-here" } } } } ``` ### Cline (VS Code) 1. Install the server (follow Installation steps above) 2. Open VS Code 3. Open the Cline extension from the left sidebar 4. Click the 'MCP Servers' icon at the top of the pane 5. Scroll to bottom and click 'Configure MCP Servers' 6. Add to the opened settings file: ```json { "mcpServers": { "n8n": { "command": "n8n-mcp-server", "env": { "N8N_HOST": "https://your-n8n-instance.com", "N8N_API_KEY": "your-api-key-here" } } } } ``` 7. Save the file 8. Ensure the MCP toggle is enabled (green) and the status indicator is green 9. Start using MCP commands in Cline ### Sage Coming soon! The n8n MCP server will be available through: - Smithery.ai marketplace - Import from Claude Desktop For now, please use Claude Desktop or Cline. ## Validation After configuration: 1. Restart your LLM application 2. Ask: "List my n8n workflows" 3. You should see your workflows listed If you get an error: - Check that your n8n instance is running - Verify your API key has correct permissions - Ensure N8N_HOST has no trailing slash ## Features ### Core Features - List and manage workflows - View workflow details - Execute workflows - Manage credentials - Handle tags and executions - Generate security audits - Manage workflow tags ### Enterprise Features These features require an n8n Enterprise license: - Project management - Variable management - Advanced user management ## Troubleshooting ### Common Issues 1. "Client not initialized" - Check N8N_HOST and N8N_API_KEY are set correctly - Ensure n8n instance is accessible - Verify API key permissions 2. "License required" - You're trying to use an Enterprise feature - Either upgrade to n8n Enterprise or use core features only 3. Connection Issues - Verify n8n instance is running - Check URL protocol (http/https) - Remove trailing slash from N8N_HOST ## Security Best Practices 1. API Key Management - Use minimal permissions necessary - Rotate keys regularly - Never commit keys to version control 2. Instance Access - Use HTTPS for production - Enable n8n authentication - Keep n8n updated ## Support - [GitHub Issues](https://github.com/illuminaresolutions/n8n-mcp-server/issues) - [n8n Documentation](https://docs.n8n.io) ## License [MIT License](LICENSE) ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "node", "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "allowJs": true }, "include": ["src/**/*"] } ``` -------------------------------------------------------------------------------- /smithery.yml: -------------------------------------------------------------------------------- ```yaml name: n8n-mcp-server version: 1.0.0 description: An MCP server that provides access to n8n workflows, executions, credentials, and more through the Model Context Protocol author: Illuminare Solutions license: MIT repository: https://github.com/illuminaresolutions/n8n-mcp-server type: mcp-server category: automation requirements: node: ">=18.0.0" installation: npm: "@illuminaresolutions/n8n-mcp-server" configuration: env: N8N_HOST: description: "Your n8n instance URL (e.g., https://your-n8n-instance.com)" required: true N8N_API_KEY: description: "Your n8n API key" required: true secret: true capabilities: - workflow-management - credential-management - execution-monitoring - tag-management - security-audit - user-management - project-management - variable-management tags: - n8n - automation - workflow - mcp - llm - ai - claude documentation: getting_started: https://github.com/illuminaresolutions/n8n-mcp-server#installation configuration: https://github.com/illuminaresolutions/n8n-mcp-server#configuration api_reference: https://github.com/illuminaresolutions/n8n-mcp-server/blob/main/N8N_API.yml ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "@illuminaresolutions/n8n-mcp-server", "description": "An MCP server that provides access to n8n workflows, executions, credentials, and more through the Model Context Protocol", "version": "1.0.0", "type": "module", "bin": { "n8n-mcp-server": "build/index.js" }, "files": [ "build", "README.md", "LICENSE" ], "license": "MIT", "author": "Illuminare Solutions", "publishConfig": { "access": "public" }, "repository": { "type": "git", "url": "git+https://github.com/illuminaresolutions/n8n-mcp-server.git" }, "bugs": { "url": "https://github.com/illuminaresolutions/n8n-mcp-server/issues" }, "homepage": "https://github.com/illuminaresolutions/n8n-mcp-server#readme", "engines": { "node": ">=18.0.0" }, "keywords": [ "n8n", "mcp", "automation", "workflow", "llm", "ai", "claude", "modelcontextprotocol" ], "scripts": { "build": "tsc && chmod +x build/index.js", "start": "node build/index.js", "prepublishOnly": "npm run build", "clean": "rm -rf build", "lint": "tsc --noEmit" }, "dependencies": { "@modelcontextprotocol/sdk": "^0.7.0", "zod": "^3.22.4", "node-fetch": "^3.3.2" }, "devDependencies": { "@types/node": "^20.11.5", "typescript": "^5.3.3" } } ``` -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- ```markdown # Development Guide ## Core Principles 1. **Keep It Simple** - Expand src/index.ts as needed - avoid complex directory structures - Focus on core functionality first - Use built-in Node.js features when possible - Check N8N_API.yml for correct endpoints/methods 2. **Document Only What Works** - No *public* "roadmap" or planned features - Only document implemented functionality - Keep documentation clear and focused 3. **JSON Requirements** - All tool arguments must be compact, single-line JSON - Example: `{"clientId":"abc123","id":"workflow123"}` 4. **Workflow Creation** - Include nodes and connections arrays (even if empty) - 'active' property is read-only ```json { "clientId": "abc123", "name": "My Workflow", "nodes": [], "connections": {} } ``` 5. **Enterprise Features** - Project/variable management require Enterprise license - Base workflow features work without license - Clear error messages for license requirements ## What Not To Do 1. **No Overcomplication** - Don't create complex directory structures - Don't add unnecessary dependencies - Use built-in fetch instead of importing it - Check if functionality exists before adding imports 2. **No Speculative Documentation** - Don't document unimplemented features - Don't maintain *public* planning documents - Don't commit planning docs as implementation 3. **No Feature Creep** - Add features only when fully designed - Follow github repo's simple approach - Focus on core functionality first 4. **No Roadmaps** - Document only what exists - Keep focus on current functionality - Clear, concise documentation ``` -------------------------------------------------------------------------------- /TYPEKIT_MCP_SDK.md: -------------------------------------------------------------------------------- ```markdown # MCP TypeScript SDK   ## Table of Contents - [Overview](#overview) - [Installation](#installation) - [Quickstart](#quickstart) - [What is MCP?](#what-is-mcp) - [Core Concepts](#core-concepts) - [Server](#server) - [Resources](#resources) - [Tools](#tools) - [Prompts](#prompts) - [Running Your Server](#running-your-server) - [stdio](#stdio) - [HTTP with SSE](#http-with-sse) - [Testing and Debugging](#testing-and-debugging) - [Examples](#examples) - [Echo Server](#echo-server) - [SQLite Explorer](#sqlite-explorer) - [Advanced Usage](#advanced-usage) - [Low-Level Server](#low-level-server) - [Writing MCP Clients](#writing-mcp-clients) - [Server Capabilities](#server-capabilities) ## Overview The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to: - Build MCP clients that can connect to any MCP server - Create MCP servers that expose resources, prompts and tools - Use standard transports like stdio and SSE - Handle all MCP protocol messages and lifecycle events ## Installation ```bash npm install @modelcontextprotocol/sdk ``` ## Quick Start Let's create a simple MCP server that exposes a calculator tool and some data: ```typescript import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; // Create an MCP server const server = new McpServer({ name: "Demo", version: "1.0.0" }); // Add an addition tool server.tool("add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }] }) ); // Add a dynamic greeting resource server.resource( "greeting", new ResourceTemplate("greeting://{name}", { list: undefined }), async (uri, { name }) => ({ contents: [{ uri: uri.href, text: `Hello, ${name}!` }] }) ); // Start receiving messages on stdin and sending messages on stdout const transport = new StdioServerTransport(); await server.connect(transport); ``` ## What is MCP? The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can: - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context) - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect) - Define interaction patterns through **Prompts** (reusable templates for LLM interactions) - And more! ## Core Concepts ### Server The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: ```typescript const server = new McpServer({ name: "My App", version: "1.0.0" }); ``` ### Resources Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects: ```typescript // Static resource server.resource( "config", "config://app", async (uri) => ({ contents: [{ uri: uri.href, text: "App configuration here" }] }) ); // Dynamic resource with parameters server.resource( "user-profile", new ResourceTemplate("users://{userId}/profile", { list: undefined }), async (uri, { userId }) => ({ contents: [{ uri: uri.href, text: `Profile data for user ${userId}` }] }) ); ``` ### Tools Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects: ```typescript // Simple tool with parameters server.tool( "calculate-bmi", { weightKg: z.number(), heightM: z.number() }, async ({ weightKg, heightM }) => ({ content: [{ type: "text", text: String(weightKg / (heightM * heightM)) }] }) ); // Async tool with external API call server.tool( "fetch-weather", { city: z.string() }, async ({ city }) => { const response = await fetch(`https://api.weather.com/${city}`); const data = await response.text(); return { content: [{ type: "text", text: data }] }; } ); ``` ### Prompts Prompts are reusable templates that help LLMs interact with your server effectively: ```typescript server.prompt( "review-code", { code: z.string() }, ({ code }) => ({ messages: [{ role: "user", content: { type: "text", text: `Please review this code:\n\n${code}` } }] }) ); ``` ## Running Your Server MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport: ### stdio For command-line tools and direct integrations: ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "example-server", version: "1.0.0" }); // ... set up server resources, tools, and prompts ... const transport = new StdioServerTransport(); await server.connect(transport); ``` ### HTTP with SSE For remote servers, start a web server with a Server-Sent Events (SSE) endpoint, and a separate endpoint for the client to send its messages to: ```typescript import express from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; const server = new McpServer({ name: "example-server", version: "1.0.0" }); // ... set up server resources, tools, and prompts ... const app = express(); app.get("/sse", async (req, res) => { const transport = new SSEServerTransport("/messages", res); await server.connect(transport); }); app.post("/messages", async (req, res) => { // Note: to support multiple simultaneous connections, these messages will // need to be routed to a specific matching transport. (This logic isn't // implemented here, for simplicity.) await transport.handlePostMessage(req, res); }); app.listen(3001); ``` ### Testing and Debugging To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information. ## Examples ### Echo Server A simple server demonstrating resources, tools, and prompts: ```typescript import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; const server = new McpServer({ name: "Echo", version: "1.0.0" }); server.resource( "echo", new ResourceTemplate("echo://{message}", { list: undefined }), async (uri, { message }) => ({ contents: [{ uri: uri.href, text: `Resource echo: ${message}` }] }) ); server.tool( "echo", { message: z.string() }, async ({ message }) => ({ content: [{ type: "text", text: `Tool echo: ${message}` }] }) ); server.prompt( "echo", { message: z.string() }, ({ message }) => ({ messages: [{ role: "user", content: { type: "text", text: `Please process this message: ${message}` } }] }) ); ``` ### SQLite Explorer A more complex example showing database integration: ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import sqlite3 from "sqlite3"; import { promisify } from "util"; import { z } from "zod"; const server = new McpServer({ name: "SQLite Explorer", version: "1.0.0" }); // Helper to create DB connection const getDb = () => { const db = new sqlite3.Database("database.db"); return { all: promisify<string, any[]>(db.all.bind(db)), close: promisify(db.close.bind(db)) }; }; server.resource( "schema", "schema://main", async (uri) => { const db = getDb(); try { const tables = await db.all( "SELECT sql FROM sqlite_master WHERE type='table'" ); return { contents: [{ uri: uri.href, text: tables.map((t: {sql: string}) => t.sql).join("\n") }] }; } finally { await db.close(); } } ); server.tool( "query", { sql: z.string() }, async ({ sql }) => { const db = getDb(); try { const results = await db.all(sql); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] }; } catch (err: unknown) { const error = err as Error; return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } finally { await db.close(); } } ); ``` ## Advanced Usage ### Low-Level Server For more control, you can use the low-level Server class directly: ```typescript import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js"; const server = new Server( { name: "example-server", version: "1.0.0" }, { capabilities: { prompts: {} } } ); server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [{ name: "example-prompt", description: "An example prompt template", arguments: [{ name: "arg1", description: "Example argument", required: true }] }] }; }); server.setRequestHandler(GetPromptRequestSchema, async (request) => { if (request.params.name !== "example-prompt") { throw new Error("Unknown prompt"); } return { description: "Example prompt", messages: [{ role: "user", content: { type: "text", text: "Example prompt text" } }] }; }); const transport = new StdioServerTransport(); await server.connect(transport); ``` ### Writing MCP Clients The SDK provides a high-level client interface: ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; const transport = new StdioClientTransport({ command: "node", args: ["server.js"] }); const client = new Client( { name: "example-client", version: "1.0.0" }, { capabilities: { prompts: {}, resources: {}, tools: {} } } ); await client.connect(transport); // List prompts const prompts = await client.listPrompts(); // Get a prompt const prompt = await client.getPrompt("example-prompt", { arg1: "value" }); // List resources const resources = await client.listResources(); // Read a resource const resource = await client.readResource("file:///example.txt"); // Call a tool const result = await client.callTool({ name: "example-tool", arguments: { arg1: "value" } }); ``` ## Documentation - [Model Context Protocol documentation](https://modelcontextprotocol.io) - [MCP Specification](https://spec.modelcontextprotocol.io) - [Example Servers](https://github.com/modelcontextprotocol/servers) ## Contributing Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk. ## License This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details. ``` -------------------------------------------------------------------------------- /N8N_API.yml: -------------------------------------------------------------------------------- ```yaml openapi: 3.0.0 info: title: n8n Public API description: n8n Public API termsOfService: https://n8n.io/legal/terms contact: email: [email protected] license: name: Sustainable Use License url: https://github.com/n8n-io/n8n/blob/master/LICENSE.md version: 1.1.1 servers: - url: /api/v1 security: - ApiKeyAuth: [] tags: - name: User description: Operations about users - name: Audit description: Operations about security audit - name: Execution description: Operations about executions - name: Workflow description: Operations about workflows - name: Credential description: Operations about credentials - name: Tags description: Operations about tags - name: SourceControl description: Operations about source control - name: Variables description: Operations about variables - name: Projects description: Operations about projects externalDocs: description: n8n API documentation url: https://docs.n8n.io/api/ paths: /audit: post: x-eov-operation-id: generateAudit x-eov-operation-handler: v1/handlers/audit/audit.handler tags: - Audit summary: Generate an audit description: Generate a security audit for your n8n instance. requestBody: required: false content: application/json: schema: type: object properties: additionalOptions: type: object properties: daysAbandonedWorkflow: type: integer description: Days for a workflow to be considered abandoned if not executed categories: type: array items: type: string enum: - credentials - database - nodes - filesystem - instance responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/audit' '401': $ref: '#/components/responses/unauthorized' '500': description: Internal server error. /credentials: post: x-eov-operation-id: createCredential x-eov-operation-handler: v1/handlers/credentials/credentials.handler tags: - Credential summary: Create a credential description: Creates a credential that can be used by nodes of the specified type. requestBody: description: Credential to be created. required: true content: application/json: schema: $ref: '#/components/schemas/credential' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/credential' '401': $ref: '#/components/responses/unauthorized' '415': description: Unsupported media type. /credentials/{id}: delete: x-eov-operation-id: deleteCredential x-eov-operation-handler: v1/handlers/credentials/credentials.handler tags: - Credential summary: Delete credential by ID description: Deletes a credential from your instance. You must be the owner of the credentials operationId: deleteCredential parameters: - name: id in: path description: The credential ID that needs to be deleted required: true schema: type: string responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/credential' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /credentials/schema/{credentialTypeName}: get: x-eov-operation-id: getCredentialType x-eov-operation-handler: v1/handlers/credentials/credentials.handler tags: - Credential summary: Show credential data schema parameters: - name: credentialTypeName in: path description: The credential type name that you want to get the schema for required: true schema: type: string responses: '200': description: Operation successful. content: application/json: schema: type: object examples: freshdeskApi: value: additionalProperties: false type: object properties: apiKey: type: string domain: type: string required: - apiKey - domain slackOAuth2Api: value: additionalProperties: false type: object properties: clientId: type: string clientSecret: type: string required: - clientId - clientSecret '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /executions: get: x-eov-operation-id: getExecutions x-eov-operation-handler: v1/handlers/executions/executions.handler tags: - Execution summary: Retrieve all executions description: Retrieve all executions from your instance. parameters: - $ref: '#/components/parameters/includeData' - name: status in: query description: Status to filter the executions by. required: false schema: type: string enum: - error - success - waiting - name: workflowId in: query description: Workflow to filter the executions by. required: false schema: type: string example: '1000' - name: projectId in: query required: false explode: false allowReserved: true schema: type: string example: VmwOO9HeTEj20kxM - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/executionList' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /executions/{id}: get: x-eov-operation-id: getExecution x-eov-operation-handler: v1/handlers/executions/executions.handler tags: - Execution summary: Retrieve an execution description: Retrieve an execution from your instance. parameters: - $ref: '#/components/parameters/executionId' - $ref: '#/components/parameters/includeData' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/execution' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' delete: x-eov-operation-id: deleteExecution x-eov-operation-handler: v1/handlers/executions/executions.handler tags: - Execution summary: Delete an execution description: Deletes an execution from your instance. parameters: - $ref: '#/components/parameters/executionId' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/execution' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /tags: post: x-eov-operation-id: createTag x-eov-operation-handler: v1/handlers/tags/tags.handler tags: - Tags summary: Create a tag description: Create a tag in your instance. requestBody: description: Created tag object. content: application/json: schema: $ref: '#/components/schemas/tag' required: true responses: '201': description: A tag object content: application/json: schema: $ref: '#/components/schemas/tag' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '409': $ref: '#/components/responses/conflict' get: x-eov-operation-id: getTags x-eov-operation-handler: v1/handlers/tags/tags.handler tags: - Tags summary: Retrieve all tags description: Retrieve all tags from your instance. parameters: - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/tagList' '401': $ref: '#/components/responses/unauthorized' /tags/{id}: get: x-eov-operation-id: getTag x-eov-operation-handler: v1/handlers/tags/tags.handler tags: - Tags summary: Retrieves a tag description: Retrieves a tag. parameters: - $ref: '#/components/parameters/tagId' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/tag' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' delete: x-eov-operation-id: deleteTag x-eov-operation-handler: v1/handlers/tags/tags.handler tags: - Tags summary: Delete a tag description: Deletes a tag. parameters: - $ref: '#/components/parameters/tagId' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/tag' '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' put: x-eov-operation-id: updateTag x-eov-operation-handler: v1/handlers/tags/tags.handler tags: - Tags summary: Update a tag description: Update a tag. parameters: - $ref: '#/components/parameters/tagId' requestBody: description: Updated tag object. content: application/json: schema: $ref: '#/components/schemas/tag' required: true responses: '200': description: Tag object content: application/json: schema: $ref: '#/components/schemas/tag' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' '409': $ref: '#/components/responses/conflict' /workflows: post: x-eov-operation-id: createWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Create a workflow description: Create a workflow in your instance. requestBody: description: Created workflow object. content: application/json: schema: $ref: '#/components/schemas/workflow' required: true responses: '200': description: A workflow object content: application/json: schema: $ref: '#/components/schemas/workflow' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' get: x-eov-operation-id: getWorkflows x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Retrieve all workflows description: Retrieve all workflows from your instance. parameters: - name: active in: query schema: type: boolean example: true - name: tags in: query required: false explode: false allowReserved: true schema: type: string example: test,production - name: name in: query required: false explode: false allowReserved: true schema: type: string example: My Workflow - name: projectId in: query required: false explode: false allowReserved: true schema: type: string example: VmwOO9HeTEj20kxM - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/workflowList' '401': $ref: '#/components/responses/unauthorized' /workflows/{id}: get: x-eov-operation-id: getWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Retrieves a workflow description: Retrieves a workflow. parameters: - $ref: '#/components/parameters/workflowId' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/workflow' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' delete: x-eov-operation-id: deleteWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Delete a workflow description: Deletes a workflow. parameters: - $ref: '#/components/parameters/workflowId' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/workflow' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' put: x-eov-operation-id: updateWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Update a workflow description: Update a workflow. parameters: - $ref: '#/components/parameters/workflowId' requestBody: description: Updated workflow object. content: application/json: schema: $ref: '#/components/schemas/workflow' required: true responses: '200': description: Workflow object content: application/json: schema: $ref: '#/components/schemas/workflow' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /workflows/{id}/activate: post: x-eov-operation-id: activateWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Activate a workflow description: Active a workflow. parameters: - $ref: '#/components/parameters/workflowId' responses: '200': description: Workflow object content: application/json: schema: $ref: '#/components/schemas/workflow' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /workflows/{id}/deactivate: post: x-eov-operation-id: deactivateWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Deactivate a workflow description: Deactivate a workflow. parameters: - $ref: '#/components/parameters/workflowId' responses: '200': description: Workflow object content: application/json: schema: $ref: '#/components/schemas/workflow' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /workflows/{id}/transfer: put: x-eov-operation-id: transferWorkflow x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Transfer a workflow to another project. description: Transfer a workflow to another project. parameters: - $ref: '#/components/parameters/workflowId' requestBody: description: Destination project information for the workflow transfer. content: application/json: schema: type: object properties: destinationProjectId: type: string description: The ID of the project to transfer the workflow to. required: - destinationProjectId required: true responses: '200': description: Operation successful. '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /credentials/{id}/transfer: put: x-eov-operation-id: transferCredential x-eov-operation-handler: v1/handlers/credentials/credentials.handler tags: - Workflow summary: Transfer a credential to another project. description: Transfer a credential to another project. parameters: - $ref: '#/components/parameters/credentialId' requestBody: description: Destination project for the credential transfer. content: application/json: schema: type: object properties: destinationProjectId: type: string description: The ID of the project to transfer the credential to. required: - destinationProjectId required: true responses: '200': description: Operation successful. '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /workflows/{id}/tags: get: x-eov-operation-id: getWorkflowTags x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Get workflow tags description: Get workflow tags. parameters: - $ref: '#/components/parameters/workflowId' responses: '200': description: List of tags content: application/json: schema: $ref: '#/components/schemas/workflowTags' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' put: x-eov-operation-id: updateWorkflowTags x-eov-operation-handler: v1/handlers/workflows/workflows.handler tags: - Workflow summary: Update tags of a workflow description: Update tags of a workflow. parameters: - $ref: '#/components/parameters/workflowId' requestBody: description: List of tags content: application/json: schema: $ref: '#/components/schemas/tagIds' required: true responses: '200': description: List of tags after add the tag content: application/json: schema: $ref: '#/components/schemas/workflowTags' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /users: get: x-eov-operation-id: getUsers x-eov-operation-handler: v1/handlers/users/users.handler.ee tags: - User summary: Retrieve all users description: Retrieve all users from your instance. Only available for the instance owner. parameters: - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' - $ref: '#/components/parameters/includeRole' - name: projectId in: query required: false explode: false allowReserved: true schema: type: string example: VmwOO9HeTEj20kxM responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/userList' '401': $ref: '#/components/responses/unauthorized' post: x-eov-operation-id: createUser x-eov-operation-handler: v1/handlers/users/users.handler.ee tags: - User summary: Create multiple users description: Create one or more users. requestBody: description: Array of users to be created. required: true content: application/json: schema: type: array items: type: object properties: email: type: string format: email role: type: string enum: - global:admin - global:member required: - email responses: '200': description: Operation successful. content: application/json: schema: type: object properties: user: type: object properties: id: type: string email: type: string inviteAcceptUrl: type: string emailSent: type: boolean error: type: string '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' /users/{id}: get: x-eov-operation-id: getUser x-eov-operation-handler: v1/handlers/users/users.handler.ee tags: - User summary: Get user by ID/Email description: Retrieve a user from your instance. Only available for the instance owner. parameters: - $ref: '#/components/parameters/userIdentifier' - $ref: '#/components/parameters/includeRole' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/user' '401': $ref: '#/components/responses/unauthorized' delete: x-eov-operation-id: deleteUser x-eov-operation-handler: v1/handlers/users/users.handler.ee tags: - User summary: Delete a user description: Delete a user from your instance. parameters: - $ref: '#/components/parameters/userIdentifier' responses: '204': description: Operation successful. '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' /users/{id}/role: patch: x-eov-operation-id: changeRole x-eov-operation-handler: v1/handlers/users/users.handler.ee tags: - User summary: Change a user's global role description: Change a user's global role parameters: - $ref: '#/components/parameters/userIdentifier' requestBody: description: New role for the user required: true content: application/json: schema: type: object properties: newRoleName: type: string enum: - global:admin - global:member required: - newRoleName responses: '200': description: Operation successful. '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' /source-control/pull: post: x-eov-operation-id: pull x-eov-operation-handler: v1/handlers/sourceControl/sourceControl.handler tags: - SourceControl summary: Pull changes from the remote repository description: Requires the Source Control feature to be licensed and connected to a repository. requestBody: description: Pull options required: true content: application/json: schema: $ref: '#/components/schemas/pull' responses: '200': description: Import result content: application/json: schema: $ref: '#/components/schemas/importResult' '400': $ref: '#/components/responses/badRequest' '409': $ref: '#/components/responses/conflict' /variables: post: x-eov-operation-id: createVariable x-eov-operation-handler: v1/handlers/variables/variables.handler tags: - Variables summary: Create a variable description: Create a variable in your instance. requestBody: description: Payload for variable to create. content: application/json: schema: $ref: '#/components/schemas/variable' required: true responses: '201': description: Operation successful. '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' get: x-eov-operation-id: getVariables x-eov-operation-handler: v1/handlers/variables/variables.handler tags: - Variables summary: Retrieve variables description: Retrieve variables from your instance. parameters: - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/variableList' '401': $ref: '#/components/responses/unauthorized' /variables/{id}: delete: x-eov-operation-id: deleteVariable x-eov-operation-handler: v1/handlers/variables/variables.handler tags: - Variables summary: Delete a variable description: Delete a variable from your instance. parameters: - $ref: '#/components/parameters/variableId' responses: '204': description: Operation successful. '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' /projects: post: x-eov-operation-id: createProject x-eov-operation-handler: v1/handlers/projects/projects.handler tags: - Projects summary: Create a project description: Create a project in your instance. requestBody: description: Payload for project to create. content: application/json: schema: $ref: '#/components/schemas/project' required: true responses: '201': description: Operation successful. '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' get: x-eov-operation-id: getProjects x-eov-operation-handler: v1/handlers/projects/projects.handler tags: - Projects summary: Retrieve projects description: Retrieve projects from your instance. parameters: - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/cursor' responses: '200': description: Operation successful. content: application/json: schema: $ref: '#/components/schemas/projectList' '401': $ref: '#/components/responses/unauthorized' /projects/{projectId}: delete: x-eov-operation-id: deleteProject x-eov-operation-handler: v1/handlers/projects/projects.handler tags: - Projects summary: Delete a project description: Delete a project from your instance. parameters: - $ref: '#/components/parameters/projectId' responses: '204': description: Operation successful. '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' put: x-eov-operation-id: updateProject x-eov-operation-handler: v1/handlers/projects/projects.handler tags: - Project summary: Update a project description: Update a project. requestBody: description: Updated project object. content: application/json: schema: $ref: '#/components/schemas/project' required: true responses: '204': description: Operation successful. '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '403': $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' components: schemas: audit: type: object properties: Credentials Risk Report: type: object example: risk: credentials sections: - title: Credentials not used in any workflow description: These credentials are not used in any workflow. Keeping unused credentials in your instance is an unneeded security risk. recommendation: Consider deleting these credentials if you no longer need them. location: - kind: credential id: '1' name: My Test Account Database Risk Report: type: object example: risk: database sections: - title: Expressions in "Execute Query" fields in SQL nodes description: This SQL node has an expression in the "Query" field of an "Execute Query" operation. Building a SQL query with an expression may lead to a SQL injection attack. recommendation: Consider using the "Query Parameters" field to pass parameters to the query or validating the input of the expression in the "Query" field.: null location: - kind: node workflowId: '1' workflowName: My Workflow nodeId: 51eb5852-ce0b-4806-b4ff-e41322a4041a nodeName: MySQL nodeType: n8n-nodes-base.mySql Filesystem Risk Report: type: object example: risk: filesystem sections: - title: Nodes that interact with the filesystem description: This node reads from and writes to any accessible file in the host filesystem. Sensitive file content may be manipulated through a node operation. recommendation: Consider protecting any sensitive files in the host filesystem or refactoring the workflow so that it does not require host filesystem interaction.: null location: - kind: node workflowId: '1' workflowName: My Workflow nodeId: 51eb5852-ce0b-4806-b4ff-e41322a4041a nodeName: Ready Binary file nodeType: n8n-nodes-base.readBinaryFile Nodes Risk Report: type: object example: risk: nodes sections: - title: Community nodes description: This node is sourced from the community. Community nodes are not vetted by the n8n team and have full access to the host system. recommendation: Consider reviewing the source code in any community nodes installed in this n8n instance and uninstalling any community nodes no longer used.: null location: - kind: community nodeType: n8n-nodes-test.test packageUrl: https://www.npmjs.com/package/n8n-nodes-test Instance Risk Report: type: object example: risk: execution sections: - title: Unprotected webhooks in instance description: These webhook nodes have the "Authentication" field set to "None" and are not directly connected to a node to validate the payload. Every unprotected webhook allows your workflow to be called by any third party who knows the webhook URL. recommendation: Consider setting the "Authentication" field to an option other than "None" or validating the payload with one of the following nodes.: null location: - kind: community nodeType: n8n-nodes-test.test packageUrl: https://www.npmjs.com/package/n8n-nodes-test credential: required: - name - type - data type: object properties: id: type: string readOnly: true example: R2DjclaysHbqn778 name: type: string example: Joe's Github Credentials type: type: string example: github data: type: object writeOnly: true example: token: ada612vad6fa5df4adf5a5dsf4389adsf76da7s createdAt: type: string format: date-time readOnly: true example: '2022-04-29T11:02:29.842Z' updatedAt: type: string format: date-time readOnly: true example: '2022-04-29T11:02:29.842Z' execution: type: object properties: id: type: number example: 1000 data: type: object finished: type: boolean example: true mode: type: string enum: - cli - error - integrated - internal - manual - retry - trigger - webhook retryOf: type: number nullable: true retrySuccessId: type: number nullable: true example: '2' startedAt: type: string format: date-time stoppedAt: type: string format: date-time workflowId: type: number example: '1000' waitTill: type: string nullable: true format: date-time customData: type: object executionList: type: object properties: data: type: array items: $ref: '#/components/schemas/execution' nextCursor: type: string description: Paginate through executions by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA tag: type: object additionalProperties: false required: - name properties: id: type: string readOnly: true example: 2tUt1wbLX592XDdX name: type: string example: Production createdAt: type: string format: date-time readOnly: true updatedAt: type: string format: date-time readOnly: true tagList: type: object properties: data: type: array items: $ref: '#/components/schemas/tag' nextCursor: type: string description: Paginate through tags by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA node: type: object additionalProperties: false properties: id: type: string example: 0f5532f9-36ba-4bef-86c7-30d607400b15 name: type: string example: Jira webhookId: type: string disabled: type: boolean notesInFlow: type: boolean notes: type: string type: type: string example: n8n-nodes-base.Jira typeVersion: type: number example: 1 executeOnce: type: boolean example: false alwaysOutputData: type: boolean example: false retryOnFail: type: boolean example: false maxTries: type: number waitBetweenTries: type: number continueOnFail: type: boolean example: false description: use onError instead deprecated: true onError: type: string example: stopWorkflow position: type: array items: type: number example: - -100 - 80 parameters: type: object example: additionalProperties: {} credentials: type: object example: jiraSoftwareCloudApi: id: '35' name: jiraApi createdAt: type: string format: date-time readOnly: true updatedAt: type: string format: date-time readOnly: true workflowSettings: type: object additionalProperties: false properties: saveExecutionProgress: type: boolean saveManualExecutions: type: boolean saveDataErrorExecution: type: string enum: - all - none saveDataSuccessExecution: type: string enum: - all - none executionTimeout: type: number example: 3600 maxLength: 3600 errorWorkflow: type: string example: VzqKEW0ShTXA5vPj description: The ID of the workflow that contains the error trigger node. timezone: type: string example: America/New_York executionOrder: type: string example: v1 workflow: type: object additionalProperties: false required: - name - nodes - connections - settings properties: id: type: string readOnly: true example: 2tUt1wbLX592XDdX name: type: string example: Workflow 1 active: type: boolean readOnly: true createdAt: type: string format: date-time readOnly: true updatedAt: type: string format: date-time readOnly: true nodes: type: array items: $ref: '#/components/schemas/node' connections: type: object example: main: - node: Jira type: main index: 0 settings: $ref: '#/components/schemas/workflowSettings' staticData: example: lastId: 1 anyOf: - type: string format: jsonString nullable: true - type: object nullable: true tags: type: array items: $ref: '#/components/schemas/tag' readOnly: true workflowList: type: object properties: data: type: array items: $ref: '#/components/schemas/workflow' nextCursor: type: string description: Paginate through workflows by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA workflowTags: type: array items: $ref: '#/components/schemas/tag' tagIds: type: array items: type: object additionalProperties: false required: - id properties: id: type: string example: 2tUt1wbLX592XDdX user: required: - email type: object properties: id: type: string readOnly: true example: 123e4567-e89b-12d3-a456-426614174000 email: type: string format: email example: [email protected] firstName: maxLength: 32 type: string description: User's first name readOnly: true example: john lastName: maxLength: 32 type: string description: User's last name readOnly: true example: Doe isPending: type: boolean description: Whether the user finished setting up their account in response to the invitation (true) or not (false). readOnly: true createdAt: type: string description: Time the user was created. format: date-time readOnly: true updatedAt: type: string description: Last time the user was updated. format: date-time readOnly: true role: type: string example: owner readOnly: true userList: type: object properties: data: type: array items: $ref: '#/components/schemas/user' nextCursor: type: string description: Paginate through users by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA pull: type: object properties: force: type: boolean example: true variables: type: object example: foo: bar importResult: type: object additionalProperties: true properties: variables: type: object properties: added: type: array items: type: string changed: type: array items: type: string credentials: type: array items: type: object properties: id: type: string name: type: string type: type: string workflows: type: array items: type: object properties: id: type: string name: type: string tags: type: object properties: tags: type: array items: type: object properties: id: type: string name: type: string mappings: type: array items: type: object properties: workflowId: type: string tagId: type: string variable: type: object additionalProperties: false required: - key - value properties: id: type: string readOnly: true key: type: string value: type: string example: test type: type: string readOnly: true variableList: type: object properties: data: type: array items: $ref: '#/components/schemas/variable' nextCursor: type: string description: Paginate through variables by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA project: type: object additionalProperties: false required: - name properties: id: type: string readOnly: true name: type: string type: type: string readOnly: true projectList: type: object properties: data: type: array items: $ref: '#/components/schemas/project' nextCursor: type: string description: Paginate through projects by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. nullable: true example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA error: required: - message type: object properties: code: type: string message: type: string description: type: string role: readOnly: true type: object properties: id: type: number readOnly: true example: 1 name: type: string example: owner readOnly: true scope: type: string readOnly: true example: global createdAt: type: string description: Time the role was created. format: date-time readOnly: true updatedAt: type: string description: Last time the role was updated. format: date-time readOnly: true credentialType: type: object properties: displayName: type: string readOnly: true example: Email name: type: string readOnly: true example: email type: type: string readOnly: true example: string default: type: string readOnly: true example: string Error: $ref: '#/components/schemas/error' Role: $ref: '#/components/schemas/role' Execution: $ref: '#/components/schemas/execution' Node: $ref: '#/components/schemas/node' Tag: $ref: '#/components/schemas/tag' Workflow: $ref: '#/components/schemas/workflow' WorkflowSettings: $ref: '#/components/schemas/workflowSettings' ExecutionList: $ref: '#/components/schemas/executionList' WorkflowList: $ref: '#/components/schemas/workflowList' Credential: $ref: '#/components/schemas/credential' CredentialType: $ref: '#/components/schemas/credentialType' Audit: $ref: '#/components/schemas/audit' Pull: $ref: '#/components/schemas/pull' ImportResult: $ref: '#/components/schemas/importResult' UserList: $ref: '#/components/schemas/userList' User: $ref: '#/components/schemas/user' responses: unauthorized: description: Unauthorized notFound: description: The specified resource was not found. badRequest: description: The request is invalid or provides malformed data. conflict: description: Conflict forbidden: description: Forbidden NotFound: $ref: '#/components/responses/notFound' Unauthorized: $ref: '#/components/responses/unauthorized' BadRequest: $ref: '#/components/responses/badRequest' Conflict: $ref: '#/components/responses/conflict' Forbidden: $ref: '#/components/responses/forbidden' parameters: includeData: name: includeData in: query description: Whether or not to include the execution's detailed data. required: false schema: type: boolean limit: name: limit in: query description: The maximum number of items to return. required: false schema: type: number example: 100 default: 100 maximum: 250 cursor: name: cursor in: query description: Paginate by setting the cursor parameter to the nextCursor attribute returned by the previous request's response. Default value fetches the first "page" of the collection. See pagination for more detail. required: false style: form schema: type: string executionId: name: id in: path description: The ID of the execution. required: true schema: type: number tagId: name: id in: path description: The ID of the tag. required: true schema: type: string workflowId: name: id in: path description: The ID of the workflow. required: true schema: type: string credentialId: name: id in: path description: The ID of the credential. required: true schema: type: string includeRole: name: includeRole in: query description: Whether to include the user's role or not. required: false schema: type: boolean example: true default: false userIdentifier: name: id in: path description: The ID or email of the user. required: true schema: type: string format: identifier variableId: name: id in: path description: The ID of the variable. required: true schema: type: string projectId: name: projectId in: path description: The ID of the project. required: true schema: type: string Cursor: $ref: '#/components/parameters/cursor' Limit: $ref: '#/components/parameters/limit' ExecutionId: $ref: '#/components/parameters/executionId' WorkflowId: $ref: '#/components/parameters/workflowId' TagId: $ref: '#/components/parameters/tagId' IncludeData: $ref: '#/components/parameters/includeData' UserIdentifier: $ref: '#/components/parameters/userIdentifier' IncludeRole: $ref: '#/components/parameters/includeRole' securitySchemes: ApiKeyAuth: type: apiKey in: header name: X-N8N-API-KEY ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import fetch from 'node-fetch'; // Type definitions for n8n API responses interface N8nUser { id: string; email: string; firstName?: string; lastName?: string; isPending: boolean; role?: string; createdAt: string; updatedAt: string; } interface N8nUserList { data: N8nUser[]; nextCursor?: string; } interface N8nWorkflow { id: number; name: string; active: boolean; createdAt: string; updatedAt: string; tags: string[]; } interface N8nWorkflowList { data: N8nWorkflow[]; nextCursor?: string; } interface N8nProject { id: string; name: string; type?: string; } interface N8nProjectList { data: N8nProject[]; nextCursor?: string; } interface N8nVariable { id: string; key: string; value: string; type?: string; } interface N8nVariableList { data: N8nVariable[]; nextCursor?: string; } interface N8nExecution { id: number; data?: any; finished: boolean; mode: string; retryOf?: number; retrySuccessId?: number; startedAt: string; stoppedAt?: string; workflowId: number; waitTill?: string; } interface N8nExecutionList { data: N8nExecution[]; nextCursor?: string; } interface N8nTag { id: string; name: string; createdAt?: string; updatedAt?: string; } interface N8nTagList { data: N8nTag[]; nextCursor?: string; } interface N8nAuditResult { 'Credentials Risk Report'?: any; 'Database Risk Report'?: any; 'Filesystem Risk Report'?: any; 'Nodes Risk Report'?: any; 'Instance Risk Report'?: any; } class N8nClient { constructor( private baseUrl: string, private apiKey: string ) { // Remove trailing slash if present this.baseUrl = baseUrl.replace(/\/$/, ''); } private async makeRequest<T>(endpoint: string, options: any = {}): Promise<T> { const url = `${this.baseUrl}/api/v1${endpoint}`; const headers = { 'X-N8N-API-KEY': this.apiKey, 'Accept': 'application/json', 'Content-Type': 'application/json', }; try { const response = await fetch(url, { ...options, headers: { ...headers, ...options.headers, }, }); if (!response.ok) { const errorText = await response.text(); let errorMessage: string; try { const errorJson = JSON.parse(errorText); // Check for license-related errors if (errorJson.message && errorJson.message.includes('license')) { errorMessage = `This operation requires an n8n Enterprise license with project management features enabled. Error: ${errorJson.message}`; } else { errorMessage = errorJson.message || errorText; } } catch { errorMessage = errorText; } throw new Error(`N8N API error: ${errorMessage}`); } // Handle 204 No Content responses if (response.status === 204) { return {} as T; } return await response.json() as T; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to connect to n8n: ${error.message}`); } throw error; } } async listWorkflows(): Promise<N8nWorkflowList> { return this.makeRequest<N8nWorkflowList>('/workflows'); } async getWorkflow(id: string): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>(`/workflows/${id}`); } async createWorkflow(name: string, nodes: any[] = [], connections: any = {}): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>('/workflows', { method: 'POST', body: JSON.stringify({ name, nodes, connections, settings: { saveManualExecutions: true, saveExecutionProgress: true, }, }), }); } async updateWorkflow(id: string, workflow: Partial<N8nWorkflow>): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>(`/workflows/${id}`, { method: 'PUT', body: JSON.stringify(workflow), }); } async deleteWorkflow(id: string): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>(`/workflows/${id}`, { method: 'DELETE', }); } async activateWorkflow(id: string): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>(`/workflows/${id}/activate`, { method: 'POST', }); } async deactivateWorkflow(id: string): Promise<N8nWorkflow> { return this.makeRequest<N8nWorkflow>(`/workflows/${id}/deactivate`, { method: 'POST', }); } // Project management methods (requires n8n Enterprise license) async listProjects(): Promise<N8nProjectList> { return this.makeRequest<N8nProjectList>('/projects'); } async createProject(name: string): Promise<void> { return this.makeRequest<void>('/projects', { method: 'POST', body: JSON.stringify({ name }), }); } async deleteProject(projectId: string): Promise<void> { return this.makeRequest<void>(`/projects/${projectId}`, { method: 'DELETE', }); } async updateProject(projectId: string, name: string): Promise<void> { return this.makeRequest<void>(`/projects/${projectId}`, { method: 'PUT', body: JSON.stringify({ name }), }); } // User management methods async listUsers(): Promise<N8nUserList> { return this.makeRequest<N8nUserList>('/users'); } async createUsers(users: Array<{ email: string; role?: 'global:admin' | 'global:member' }>): Promise<any> { return this.makeRequest('/users', { method: 'POST', body: JSON.stringify(users), }); } async getUser(idOrEmail: string): Promise<N8nUser> { return this.makeRequest<N8nUser>(`/users/${idOrEmail}`); } async deleteUser(idOrEmail: string): Promise<void> { return this.makeRequest<void>(`/users/${idOrEmail}`, { method: 'DELETE', }); } // Variable management methods async listVariables(): Promise<N8nVariableList> { return this.makeRequest<N8nVariableList>('/variables'); } async createVariable(key: string, value: string): Promise<void> { return this.makeRequest<void>('/variables', { method: 'POST', body: JSON.stringify({ key, value }), }); } async deleteVariable(id: string): Promise<void> { return this.makeRequest<void>(`/variables/${id}`, { method: 'DELETE', }); } // Execution management methods async getExecutions(options: { includeData?: boolean; status?: 'error' | 'success' | 'waiting'; workflowId?: string; limit?: number; } = {}): Promise<N8nExecutionList> { const params = new URLSearchParams(); if (options.includeData !== undefined) params.append('includeData', String(options.includeData)); if (options.status) params.append('status', options.status); if (options.workflowId) params.append('workflowId', options.workflowId); if (options.limit) params.append('limit', String(options.limit)); return this.makeRequest<N8nExecutionList>(`/executions?${params.toString()}`); } async getExecution(id: number, includeData: boolean = false): Promise<N8nExecution> { const params = new URLSearchParams(); if (includeData) params.append('includeData', 'true'); return this.makeRequest<N8nExecution>(`/executions/${id}?${params.toString()}`); } async deleteExecution(id: number): Promise<N8nExecution> { return this.makeRequest<N8nExecution>(`/executions/${id}`, { method: 'DELETE', }); } // Tag management methods async createTag(name: string): Promise<N8nTag> { return this.makeRequest<N8nTag>('/tags', { method: 'POST', body: JSON.stringify({ name }), }); } async getTags(options: { limit?: number } = {}): Promise<N8nTagList> { const params = new URLSearchParams(); if (options.limit) params.append('limit', String(options.limit)); return this.makeRequest<N8nTagList>(`/tags?${params.toString()}`); } async getTag(id: string): Promise<N8nTag> { return this.makeRequest<N8nTag>(`/tags/${id}`); } async updateTag(id: string, name: string): Promise<N8nTag> { return this.makeRequest<N8nTag>(`/tags/${id}`, { method: 'PUT', body: JSON.stringify({ name }), }); } async deleteTag(id: string): Promise<N8nTag> { return this.makeRequest<N8nTag>(`/tags/${id}`, { method: 'DELETE', }); } async getWorkflowTags(workflowId: string): Promise<N8nTag[]> { return this.makeRequest<N8nTag[]>(`/workflows/${workflowId}/tags`); } async updateWorkflowTags(workflowId: string, tagIds: { id: string }[]): Promise<N8nTag[]> { return this.makeRequest<N8nTag[]>(`/workflows/${workflowId}/tags`, { method: 'PUT', body: JSON.stringify(tagIds), }); } // Security audit method async generateAudit(options: { daysAbandonedWorkflow?: number; categories?: Array<'credentials' | 'database' | 'nodes' | 'filesystem' | 'instance'>; } = {}): Promise<N8nAuditResult> { return this.makeRequest<N8nAuditResult>('/audit', { method: 'POST', body: JSON.stringify({ additionalOptions: { daysAbandonedWorkflow: options.daysAbandonedWorkflow, categories: options.categories, }, }), }); } // Credential management methods async createCredential(name: string, type: string, data: Record<string, any>): Promise<any> { return this.makeRequest('/credentials', { method: 'POST', body: JSON.stringify({ name, type, data }), }); } async deleteCredential(id: string): Promise<any> { return this.makeRequest(`/credentials/${id}`, { method: 'DELETE', }); } async getCredentialSchema(credentialTypeName: string): Promise<any> { return this.makeRequest(`/credentials/schema/${credentialTypeName}`); } } // Create an MCP server const server = new Server( { name: "n8n-integration", version: "1.0.0" }, { capabilities: { tools: {} } } ); // Store client instances const clients = new Map<string, N8nClient>(); // List tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "init-n8n", description: "Initialize connection to n8n instance. Use this tool whenever an n8n URL and API key are shared to establish the connection. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { url: { type: "string" }, apiKey: { type: "string" } }, required: ["url", "apiKey"] } }, { name: "list-workflows", description: "List all workflows from n8n. Use after init-n8n to see available workflows. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" } }, required: ["clientId"] } }, { name: "get-workflow", description: "Retrieve a workflow by ID. Use after list-workflows to get detailed information about a specific workflow. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "create-workflow", description: "Create a new workflow in n8n. Use to set up a new workflow with optional nodes and connections. IMPORTANT: 1) Arguments must be provided as compact, single-line JSON without whitespace or newlines. 2) Must provide full workflow structure including nodes and connections arrays, even if empty. The 'active' property should not be included as it is read-only.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, name: { type: "string" }, nodes: { type: "array" }, connections: { type: "object" } }, required: ["clientId", "name"] } }, { name: "update-workflow", description: "Update an existing workflow in n8n. Use after get-workflow to modify a workflow's properties, nodes, or connections. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" }, workflow: { type: "object", properties: { name: { type: "string" }, active: { type: "boolean" }, nodes: { type: "array" }, connections: { type: "object" }, settings: { type: "object" } } } }, required: ["clientId", "id", "workflow"] } }, { name: "delete-workflow", description: "Delete a workflow by ID. This action cannot be undone. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "activate-workflow", description: "Activate a workflow by ID. This will enable the workflow to run. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "deactivate-workflow", description: "Deactivate a workflow by ID. This will prevent the workflow from running. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "list-projects", description: "List all projects from n8n. NOTE: Requires n8n Enterprise license with project management features enabled. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" } }, required: ["clientId"] } }, { name: "create-project", description: "Create a new project in n8n. NOTE: Requires n8n Enterprise license with project management features enabled. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, name: { type: "string" } }, required: ["clientId", "name"] } }, { name: "delete-project", description: "Delete a project by ID. NOTE: Requires n8n Enterprise license with project management features enabled. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, projectId: { type: "string" } }, required: ["clientId", "projectId"] } }, { name: "update-project", description: "Update a project's name. NOTE: Requires n8n Enterprise license with project management features enabled. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, projectId: { type: "string" }, name: { type: "string" } }, required: ["clientId", "projectId", "name"] } }, { name: "list-users", description: "Retrieve all users from your instance. Only available for the instance owner.", inputSchema: { type: "object", properties: { clientId: { type: "string" } }, required: ["clientId"] } }, { name: "create-users", description: "Create one or more users in your instance.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, users: { type: "array", items: { type: "object", properties: { email: { type: "string" }, role: { type: "string", enum: ["global:admin", "global:member"] } }, required: ["email"] } } }, required: ["clientId", "users"] } }, { name: "get-user", description: "Get user by ID or email address.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, idOrEmail: { type: "string" } }, required: ["clientId", "idOrEmail"] } }, { name: "delete-user", description: "Delete a user from your instance.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, idOrEmail: { type: "string" } }, required: ["clientId", "idOrEmail"] } }, { name: "list-variables", description: "List all variables from n8n. NOTE: Requires n8n Enterprise license with variable management features enabled. Use after init-n8n to see available variables. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" } }, required: ["clientId"] } }, { name: "create-variable", description: "Create a new variable in n8n. NOTE: Requires n8n Enterprise license with variable management features enabled. Variables can be used across workflows to store and share data. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, key: { type: "string" }, value: { type: "string" } }, required: ["clientId", "key", "value"] } }, { name: "delete-variable", description: "Delete a variable by ID. NOTE: Requires n8n Enterprise license with variable management features enabled. Use after list-variables to get the ID of the variable to delete. This action cannot be undone. IMPORTANT: Arguments must be provided as compact, single-line JSON without whitespace or newlines.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "create-credential", description: "Create a credential that can be used by nodes of the specified type. The credential type name can be found in the n8n UI when creating credentials (e.g., 'cloudflareApi', 'githubApi', 'slackOAuth2Api'). Use get-credential-schema first to see what fields are required for the credential type you want to create.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, name: { type: "string" }, type: { type: "string" }, data: { type: "object" } }, required: ["clientId", "name", "type", "data"] } }, { name: "delete-credential", description: "Delete a credential by ID. You must be the owner of the credentials.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "get-credential-schema", description: "Show credential data schema for a specific credential type. The credential type name can be found in the n8n UI when creating credentials (e.g., 'cloudflareApi', 'githubApi', 'slackOAuth2Api'). This will show you what fields are required for creating credentials of this type.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, credentialTypeName: { type: "string" } }, required: ["clientId", "credentialTypeName"] } }, // Execution Management Tools { name: "list-executions", description: "Retrieve all executions from your instance with optional filtering.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, includeData: { type: "boolean" }, status: { type: "string", enum: ["error", "success", "waiting"] }, workflowId: { type: "string" }, limit: { type: "number" } }, required: ["clientId"] } }, { name: "get-execution", description: "Retrieve a specific execution by ID.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "number" }, includeData: { type: "boolean" } }, required: ["clientId", "id"] } }, { name: "delete-execution", description: "Delete a specific execution by ID.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "number" } }, required: ["clientId", "id"] } }, // Tag Management Tools { name: "create-tag", description: "Create a new tag in your instance.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, name: { type: "string" } }, required: ["clientId", "name"] } }, { name: "list-tags", description: "Retrieve all tags from your instance.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, limit: { type: "number" } }, required: ["clientId"] } }, { name: "get-tag", description: "Retrieve a specific tag by ID.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "update-tag", description: "Update a tag's name.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" }, name: { type: "string" } }, required: ["clientId", "id", "name"] } }, { name: "delete-tag", description: "Delete a tag by ID.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, id: { type: "string" } }, required: ["clientId", "id"] } }, { name: "get-workflow-tags", description: "Get tags associated with a workflow.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, workflowId: { type: "string" } }, required: ["clientId", "workflowId"] } }, { name: "update-workflow-tags", description: "Update tags associated with a workflow.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, workflowId: { type: "string" }, tagIds: { type: "array", items: { type: "object", properties: { id: { type: "string" } }, required: ["id"] } } }, required: ["clientId", "workflowId", "tagIds"] } }, // Security Audit Tool { name: "generate-audit", description: "Generate a security audit for your n8n instance.", inputSchema: { type: "object", properties: { clientId: { type: "string" }, daysAbandonedWorkflow: { type: "number" }, categories: { type: "array", items: { type: "string", enum: ["credentials", "database", "nodes", "filesystem", "instance"] } } }, required: ["clientId"] } } ] }; }); // Tool execution handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case "init-n8n": { const { url, apiKey } = args as { url: string; apiKey: string }; try { const client = new N8nClient(url, apiKey); // Test connection by listing workflows await client.listWorkflows(); // Generate a unique client ID const clientId = Buffer.from(url).toString('base64'); clients.set(clientId, client); return { content: [{ type: "text", text: `Successfully connected to n8n at ${url}. Use this client ID for future operations: ${clientId}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "list-workflows": { const { clientId } = args as { clientId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflows = await client.listWorkflows(); const formattedWorkflows = workflows.data.map(wf => ({ id: wf.id, name: wf.name, active: wf.active, created: wf.createdAt, updated: wf.updatedAt, tags: wf.tags, })); return { content: [{ type: "text", text: JSON.stringify(formattedWorkflows, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-workflow": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflow = await client.getWorkflow(id); return { content: [{ type: "text", text: JSON.stringify(workflow, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "update-workflow": { const { clientId, id, workflow } = args as { clientId: string; id: string; workflow: Partial<N8nWorkflow>; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const updatedWorkflow = await client.updateWorkflow(id, workflow); return { content: [{ type: "text", text: `Successfully updated workflow:\n${JSON.stringify(updatedWorkflow, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "create-workflow": { const { clientId, name, nodes = [], connections = {} } = args as { clientId: string; name: string; nodes?: any[]; connections?: Record<string, any>; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflow = await client.createWorkflow(name, nodes, connections); return { content: [{ type: "text", text: `Successfully created workflow:\n${JSON.stringify(workflow, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-workflow": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflow = await client.deleteWorkflow(id); return { content: [{ type: "text", text: `Successfully deleted workflow:\n${JSON.stringify(workflow, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "activate-workflow": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflow = await client.activateWorkflow(id); return { content: [{ type: "text", text: `Successfully activated workflow:\n${JSON.stringify(workflow, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "deactivate-workflow": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const workflow = await client.deactivateWorkflow(id); return { content: [{ type: "text", text: `Successfully deactivated workflow:\n${JSON.stringify(workflow, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "list-projects": { const { clientId } = args as { clientId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const projects = await client.listProjects(); return { content: [{ type: "text", text: JSON.stringify(projects.data, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "create-project": { const { clientId, name } = args as { clientId: string; name: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.createProject(name); return { content: [{ type: "text", text: `Successfully created project: ${name}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-project": { const { clientId, projectId } = args as { clientId: string; projectId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.deleteProject(projectId); return { content: [{ type: "text", text: `Successfully deleted project with ID: ${projectId}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "update-project": { const { clientId, projectId, name } = args as { clientId: string; projectId: string; name: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.updateProject(projectId, name); return { content: [{ type: "text", text: `Successfully updated project ${projectId} with new name: ${name}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "list-users": { const { clientId } = args as { clientId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const users = await client.listUsers(); return { content: [{ type: "text", text: JSON.stringify(users.data, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "create-users": { const { clientId, users } = args as { clientId: string; users: Array<{ email: string; role?: 'global:admin' | 'global:member' }> }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const result = await client.createUsers(users); return { content: [{ type: "text", text: JSON.stringify(result, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-user": { const { clientId, idOrEmail } = args as { clientId: string; idOrEmail: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const user = await client.getUser(idOrEmail); return { content: [{ type: "text", text: JSON.stringify(user, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-user": { const { clientId, idOrEmail } = args as { clientId: string; idOrEmail: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.deleteUser(idOrEmail); return { content: [{ type: "text", text: `Successfully deleted user: ${idOrEmail}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "list-variables": { const { clientId } = args as { clientId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const variables = await client.listVariables(); return { content: [{ type: "text", text: JSON.stringify(variables.data, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "create-variable": { const { clientId, key, value } = args as { clientId: string; key: string; value: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.createVariable(key, value); return { content: [{ type: "text", text: `Successfully created variable with key: ${key}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-variable": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { await client.deleteVariable(id); return { content: [{ type: "text", text: `Successfully deleted variable with ID: ${id}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "create-credential": { const { clientId, name, type, data } = args as { clientId: string; name: string; type: string; data: Record<string, any>; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const credential = await client.createCredential(name, type, data); return { content: [{ type: "text", text: `Successfully created credential:\n${JSON.stringify(credential, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-credential": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const result = await client.deleteCredential(id); return { content: [{ type: "text", text: `Successfully deleted credential:\n${JSON.stringify(result, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-credential-schema": { const { clientId, credentialTypeName } = args as { clientId: string; credentialTypeName: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const schema = await client.getCredentialSchema(credentialTypeName); return { content: [{ type: "text", text: JSON.stringify(schema, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } // Execution Management Handlers case "list-executions": { const { clientId, includeData, status, workflowId, limit } = args as { clientId: string; includeData?: boolean; status?: 'error' | 'success' | 'waiting'; workflowId?: string; limit?: number; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const executions = await client.getExecutions({ includeData, status, workflowId, limit }); return { content: [{ type: "text", text: JSON.stringify(executions.data, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-execution": { const { clientId, id, includeData } = args as { clientId: string; id: number; includeData?: boolean }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const execution = await client.getExecution(id, includeData); return { content: [{ type: "text", text: JSON.stringify(execution, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-execution": { const { clientId, id } = args as { clientId: string; id: number }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const execution = await client.deleteExecution(id); return { content: [{ type: "text", text: `Successfully deleted execution:\n${JSON.stringify(execution, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } // Tag Management Handlers case "create-tag": { const { clientId, name } = args as { clientId: string; name: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tag = await client.createTag(name); return { content: [{ type: "text", text: `Successfully created tag:\n${JSON.stringify(tag, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "list-tags": { const { clientId, limit } = args as { clientId: string; limit?: number }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tags = await client.getTags({ limit }); return { content: [{ type: "text", text: JSON.stringify(tags.data, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-tag": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tag = await client.getTag(id); return { content: [{ type: "text", text: JSON.stringify(tag, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "update-tag": { const { clientId, id, name } = args as { clientId: string; id: string; name: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tag = await client.updateTag(id, name); return { content: [{ type: "text", text: `Successfully updated tag:\n${JSON.stringify(tag, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "delete-tag": { const { clientId, id } = args as { clientId: string; id: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tag = await client.deleteTag(id); return { content: [{ type: "text", text: `Successfully deleted tag:\n${JSON.stringify(tag, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "get-workflow-tags": { const { clientId, workflowId } = args as { clientId: string; workflowId: string }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tags = await client.getWorkflowTags(workflowId); return { content: [{ type: "text", text: JSON.stringify(tags, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "update-workflow-tags": { const { clientId, workflowId, tagIds } = args as { clientId: string; workflowId: string; tagIds: { id: string }[]; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const tags = await client.updateWorkflowTags(workflowId, tagIds); return { content: [{ type: "text", text: `Successfully updated workflow tags:\n${JSON.stringify(tags, null, 2)}`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } case "generate-audit": { const { clientId, daysAbandonedWorkflow, categories } = args as { clientId: string; daysAbandonedWorkflow?: number; categories?: Array<'credentials' | 'database' | 'nodes' | 'filesystem' | 'instance'>; }; const client = clients.get(clientId); if (!client) { return { content: [{ type: "text", text: "Client not initialized. Please run init-n8n first.", }], isError: true }; } try { const audit = await client.generateAudit({ daysAbandonedWorkflow, categories }); return { content: [{ type: "text", text: JSON.stringify(audit, null, 2), }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred", }], isError: true }; } } default: return { content: [{ type: "text", text: `Unknown tool: ${name}`, }], isError: true }; } }); // Start the server const transport = new StdioServerTransport(); await server.connect(transport); console.error("N8N MCP Server running on stdio"); ```