#
tokens: 29298/50000 9/10 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
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 ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)

## 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");

```
Page 1/2FirstPrevNextLast