#
tokens: 49945/50000 70/86 files (page 1/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 3. Use http://codebase.md/leonardsellem/n8n-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .babelrc
├── .env.example
├── .eslintrc.json
├── .github
│   └── workflows
│       ├── claude-code-review.yml
│       ├── claude.yml
│       ├── docker-publish.yml
│       └── release-package.yml
├── .gitignore
├── AGENTS.md
├── babel.config.cjs
├── CLAUDE.md
├── Dockerfile
├── docs
│   ├── .gitkeep
│   ├── api
│   │   ├── dynamic-resources.md
│   │   ├── execution-tools.md
│   │   ├── index.md
│   │   ├── static-resources.md
│   │   └── workflow-tools.md
│   ├── development
│   │   ├── architecture.md
│   │   ├── extending.md
│   │   ├── index.md
│   │   └── testing.md
│   ├── examples
│   │   ├── advanced-scenarios.md
│   │   ├── basic-examples.md
│   │   ├── index.md
│   │   └── integration-examples.md
│   ├── images
│   │   ├── architecture.png.placeholder
│   │   └── n8n-api-key.png.placeholder
│   ├── index.md
│   └── setup
│       ├── configuration.md
│       ├── index.md
│       ├── installation.md
│       └── troubleshooting.md
├── jest.config.cjs
├── LICENSE
├── manual_verify_update.mjs
├── n8n-openapi.yml
├── package-lock.json
├── package.json
├── README.md
├── requirements.txt
├── run-tests.js
├── smithery.yaml
├── src
│   ├── .gitkeep
│   ├── api
│   │   ├── client.ts
│   │   └── n8n-client.ts
│   ├── config
│   │   ├── environment.ts
│   │   └── server.ts
│   ├── errors
│   │   ├── error-codes.ts
│   │   └── index.ts
│   ├── index.ts
│   ├── resources
│   │   ├── dynamic
│   │   │   ├── execution.ts
│   │   │   └── workflow.ts
│   │   ├── index.ts
│   │   └── static
│   │       ├── execution-stats.ts
│   │       └── workflows.ts
│   ├── tools
│   │   ├── execution
│   │   │   ├── base-handler.ts
│   │   │   ├── delete.ts
│   │   │   ├── get.ts
│   │   │   ├── handler.ts
│   │   │   ├── index.ts
│   │   │   ├── list.ts
│   │   │   └── run.ts
│   │   └── workflow
│   │       ├── activate.ts
│   │       ├── base-handler.ts
│   │       ├── create.ts
│   │       ├── deactivate.ts
│   │       ├── delete.ts
│   │       ├── get.ts
│   │       ├── handler.ts
│   │       ├── index.ts
│   │       ├── list.ts
│   │       └── update.ts
│   ├── types
│   │   └── index.ts
│   └── utils
│       ├── execution-formatter.ts
│       └── resource-formatter.ts
├── tests
│   ├── jest-globals.d.ts
│   ├── mocks
│   │   ├── axios-mock.ts
│   │   └── n8n-fixtures.ts
│   ├── README.md
│   ├── test-setup.ts
│   ├── tsconfig.json
│   └── unit
│       ├── api
│       │   ├── client.test.ts.bak
│       │   └── simple-client.test.ts
│       ├── config
│       │   ├── environment.test.ts
│       │   ├── environment.test.ts.bak
│       │   └── simple-environment.test.ts
│       ├── resources
│       │   └── dynamic
│       │       └── workflow.test.ts
│       ├── tools
│       │   └── workflow
│       │       ├── list.test.ts.bak
│       │       └── simple-tool.test.ts
│       └── utils
│           ├── execution-formatter.test.ts
│           └── resource-formatter.test.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/docs/.gitkeep:
--------------------------------------------------------------------------------

```
1 | 
```

--------------------------------------------------------------------------------
/src/.gitkeep:
--------------------------------------------------------------------------------

```
1 | 
```

--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------

```
1 | {
2 |   "presets": [
3 |     ["@babel/preset-env", { "targets": { "node": "current" } }],
4 |     "@babel/preset-typescript"
5 |   ]
6 | }
7 | 
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
 1 | # n8n MCP Server Environment Variables
 2 | 
 3 | # Required: URL of the n8n API (e.g., http://localhost:5678/api/v1)
 4 | N8N_API_URL=http://localhost:5678/api/v1
 5 | 
 6 | # Required: API key for authenticating with n8n
 7 | # Generate this in the n8n UI under Settings > API > API Keys
 8 | N8N_API_KEY=your_n8n_api_key_here
 9 | 
10 | # Optional: Set to 'true' to enable debug logging
11 | DEBUG=false
12 | 
```

--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": [
 3 |     "eslint:recommended",
 4 |     "plugin:@typescript-eslint/recommended"
 5 |   ],
 6 |   "parser": "@typescript-eslint/parser",
 7 |   "plugins": [
 8 |     "@typescript-eslint"
 9 |   ],
10 |   "root": true,
11 |   "rules": {
12 |     "@typescript-eslint/no-explicit-any": "off",
13 |     "@typescript-eslint/no-unused-vars": [
14 |       "error",
15 |       {
16 |         "argsIgnorePattern": "^_",
17 |         "varsIgnorePattern": "^_"
18 |       }
19 |     ]
20 |   }
21 | }
22 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependency directories
 2 | node_modules/
 3 | coverage/
 4 | 
 5 | # Environment variables
 6 | .env
 7 | .env.local
 8 | .env.development.local
 9 | .env.test.local
10 | .env.production.local
11 | 
12 | # Build outputs
13 | dist/
14 | build/
15 | 
16 | # Logs
17 | logs
18 | *.log
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 | 
23 | # IDE - VSCode
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | 
30 | # macOS
31 | .DS_Store
32 | 
33 | # Misc
34 | .npm
35 | .eslintcache
36 | .yarn-integrity
37 | 
38 | # CRCT System
39 | cline_docs/
40 | strategy_tasks/
41 | Previous_versions/
42 | --output
43 | test-output-summary.md
44 | 
45 | # Python
46 | __pycache__/
47 | *.py[cod]
48 | *$py.class
49 | *.so
50 | .Python
51 | venv/
52 | ENV/
53 | env/
54 | .env/
55 | .venv/
56 | *.egg-info/
57 | dist/
58 | build/
59 | 
60 | # Embeddings
61 | *.embedding
62 | embeddings/
63 | mcp-config.json
```

--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Testing System for n8n MCP Server
  2 | 
  3 | This directory contains the testing framework and tests for the n8n MCP Server project. The tests are organized in a hierarchical structure to match the project's architecture.
  4 | 
  5 | ## Test Structure
  6 | 
  7 | - **unit/**: Unit tests for individual components
  8 |   - **api/**: Tests for API clients and services
  9 |   - **config/**: Tests for configuration handling
 10 |   - **errors/**: Tests for error handling
 11 |   - **resources/**: Tests for MCP resource handlers
 12 |     - **dynamic/**: Tests for dynamic resource handlers
 13 |     - **static/**: Tests for static resource handlers
 14 |   - **tools/**: Tests for MCP tool handlers
 15 |     - **workflow/**: Tests for workflow-related tools
 16 |     - **execution/**: Tests for execution-related tools
 17 |   - **utils/**: Tests for utility functions
 18 | 
 19 | - **integration/**: Integration tests for component interactions
 20 |   - Tests that verify multiple components work together correctly
 21 | 
 22 | - **e2e/**: End-to-end tests for full server functionality
 23 |   - Tests that simulate real-world usage scenarios
 24 | 
 25 | - **mocks/**: Mock data and utilities for testing
 26 |   - Reusable mock data and functions shared across tests
 27 | 
 28 | ## Running Tests
 29 | 
 30 | The project uses Jest as the test runner with ESM support. The following npm scripts are available:
 31 | 
 32 | ```bash
 33 | # Run all tests
 34 | npm test
 35 | 
 36 | # Run tests in watch mode (useful during development)
 37 | npm run test:watch
 38 | 
 39 | # Run tests with coverage report
 40 | npm run test:coverage
 41 | 
 42 | # Run specific test file(s)
 43 | npm test -- tests/unit/api/client.test.ts
 44 | 
 45 | # Run tests matching a specific pattern
 46 | npm test -- -t "should format and return workflows"
 47 | ```
 48 | 
 49 | ## Writing Tests
 50 | 
 51 | ### Test File Naming Convention
 52 | 
 53 | - All test files should end with `.test.ts`
 54 | - Test files should be placed in the same directory structure as the source files they test
 55 | 
 56 | ### Test Organization
 57 | 
 58 | Each test file should follow this structure:
 59 | 
 60 | ```typescript
 61 | /**
 62 |  * Description of what's being tested
 63 |  */
 64 | 
 65 | import '@jest/globals';
 66 | import { ComponentToTest } from '../../../src/path/to/component.js';
 67 | // Import other dependencies and mocks
 68 | 
 69 | // Mock dependencies
 70 | jest.mock('../../../src/path/to/dependency.js');
 71 | 
 72 | describe('ComponentName', () => {
 73 |   // Setup and teardown
 74 |   beforeEach(() => {
 75 |     // Common setup
 76 |   });
 77 |   
 78 |   afterEach(() => {
 79 |     // Common cleanup
 80 |   });
 81 |   
 82 |   describe('methodName', () => {
 83 |     it('should do something specific', () => {
 84 |       // Arrange
 85 |       // ...
 86 |       
 87 |       // Act
 88 |       // ...
 89 |       
 90 |       // Assert
 91 |       expect(result).toBe(expectedValue);
 92 |     });
 93 |     
 94 |     // More test cases...
 95 |   });
 96 |   
 97 |   // More method tests...
 98 | });
 99 | ```
100 | 
101 | ### Testing Utilities
102 | 
103 | The project provides several testing utilities:
104 | 
105 | - **test-setup.ts**: Common setup for all tests
106 | - **mocks/axios-mock.ts**: Utilities for mocking Axios HTTP requests
107 | - **mocks/n8n-fixtures.ts**: Mock data for n8n API responses
108 | 
109 | ## Best Practices
110 | 
111 | 1. **Isolation**: Each test should be independent and not rely on other tests
112 | 2. **Mock Dependencies**: External dependencies should be mocked
113 | 3. **Descriptive Names**: Use descriptive test and describe names
114 | 4. **Arrange-Act-Assert**: Structure your tests with clear sections
115 | 5. **Coverage**: Aim for high test coverage, especially for critical paths
116 | 6. **Readability**: Write clear, readable tests that serve as documentation
117 | 
118 | ## Extending the Test Suite
119 | 
120 | When adding new functionality to the project:
121 | 
122 | 1. Create corresponding test files in the appropriate directory
123 | 2. Use existing mocks and utilities when possible
124 | 3. Create new mock data in `mocks/` for reusability
125 | 4. Update this README if you add new testing patterns or utilities
126 | 
127 | ## Troubleshooting
128 | 
129 | If you encounter issues running the tests:
130 | 
131 | - Ensure you're using Node.js 20 or later
132 | - Run `npm install` to ensure all dependencies are installed
133 | - Check for ESM compatibility issues if importing CommonJS modules
134 | - Use `console.log` or `console.error` for debugging (removed in production)
135 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # n8n MCP Server
  2 | 
  3 | [![npm version](https://badge.fury.io/js/%40leonardsellem%2Fn8n-mcp-server.svg)](https://badge.fury.io/js/%40leonardsellem%2Fn8n-mcp-server)
  4 | 
  5 | A Model Context Protocol (MCP) server that allows AI assistants to interact with n8n workflows through natural language.
  6 | 
  7 | ## Overview
  8 | 
  9 | This project provides a Model Context Protocol (MCP) server that empowers AI assistants to seamlessly interact with n8n, a popular workflow automation tool. It acts as a bridge, enabling AI assistants to programmatically manage and control n8n workflows and executions using natural language commands.
 10 | 
 11 | ## Installation
 12 | 
 13 | ### Prerequisites
 14 | 
 15 | - Node.js 20 or later
 16 | - n8n instance with API access enabled
 17 | 
 18 | ### Install from npm
 19 | 
 20 | ```bash
 21 | npm install -g @leonardsellem/n8n-mcp-server
 22 | ```
 23 | 
 24 | ### Install from source
 25 | 
 26 | ```bash
 27 | # Clone the repository
 28 | git clone https://github.com/leonardsellem/n8n-mcp-server.git
 29 | cd n8n-mcp-server
 30 | 
 31 | # Install dependencies
 32 | npm install
 33 | 
 34 | # Build the project
 35 | npm run build
 36 | 
 37 | # Optional: Install globally
 38 | npm install -g .
 39 | ```
 40 | 
 41 | ### Docker Installation
 42 | 
 43 | You can also run the server using Docker:
 44 | 
 45 | ```bash
 46 | # Pull the image
 47 | docker pull leonardsellem/n8n-mcp-server
 48 | 
 49 | # Run the container with your n8n API configuration
 50 | docker run -e N8N_API_URL=http://your-n8n:5678/api/v1 \
 51 |   -e N8N_API_KEY=your_n8n_api_key \
 52 |   -e N8N_WEBHOOK_USERNAME=username \
 53 |   -e N8N_WEBHOOK_PASSWORD=password \
 54 |   leonardsellem/n8n-mcp-server
 55 | ```
 56 | 
 57 | ## Updating the Server
 58 | 
 59 | How you update the server depends on how you initially installed it.
 60 | 
 61 | ### 1. Installed globally via npm
 62 | 
 63 | If you installed the server using `npm install -g @leonardsellem/n8n-mcp-server`:
 64 | 
 65 | 1.  Open your terminal or command prompt.
 66 | 2.  Run the following command to get the latest version:
 67 |     ```bash
 68 |     npm install -g @leonardsellem/n8n-mcp-server@latest
 69 |     ```
 70 | 3.  If the server is currently running (e.g., as a background process or service), you'll need to restart it for the changes to take effect.
 71 | 
 72 | ### 2. Installed from source
 73 | 
 74 | If you cloned the repository and installed from source:
 75 | 
 76 | 1.  Open your terminal or command prompt.
 77 | 2.  Navigate to the directory where you cloned the project:
 78 |     ```bash
 79 |     cd path/to/n8n-mcp-server
 80 |     ```
 81 | 3.  If you've made any local changes to the code that you want to keep, consider stashing them (optional):
 82 |     ```bash
 83 |     git stash
 84 |     ```
 85 |     You can apply them later with `git stash pop`.
 86 | 4.  Pull the latest changes from the repository (assuming you are on the `main` branch):
 87 |     ```bash
 88 |     git pull origin main
 89 |     ```
 90 |     If you are on a different branch, replace `main` with your branch name.
 91 | 5.  Install or update any changed dependencies:
 92 |     ```bash
 93 |     npm install
 94 |     ```
 95 | 6.  Rebuild the project to include the latest updates:
 96 |     ```bash
 97 |     npm run build
 98 |     ```
 99 | 7.  If you previously installed it globally from this source folder using `npm install -g .`, you might want to run this command again to update the global link:
100 |     ```bash
101 |     npm install -g .
102 |     ```
103 | 8.  Restart the server.
104 |     *   If you run the server directly using a command like `node build/index.js` in your AI assistant's MCP configuration, ensure the path is still correct. Using `npm install -g .` and then `n8n-mcp-server` as the command should keep this consistent.
105 | 
106 | ### 3. Using Docker
107 | 
108 | If you are running the server using Docker:
109 | 
110 | 1.  Pull the latest image from Docker Hub:
111 |     ```bash
112 |     docker pull leonardsellem/n8n-mcp-server:latest
113 |     ```
114 | 2.  Stop and remove your old container. You'll need your container's name or ID (you can find it using `docker ps`):
115 |     ```bash
116 |     docker stop <your_container_name_or_id>
117 |     docker rm <your_container_name_or_id>
118 |     ```
119 | 3.  Start a new container with the updated image. Use the same `docker run` command you used previously, including all your necessary environment variables (refer to the "Docker Installation" section for an example command). For instance:
120 |     ```bash
121 |     docker run -e N8N_API_URL=http://your-n8n:5678/api/v1 \
122 |       -e N8N_API_KEY=your_n8n_api_key \
123 |       -e N8N_WEBHOOK_USERNAME=username \
124 |       -e N8N_WEBHOOK_PASSWORD=password \
125 |       leonardsellem/n8n-mcp-server:latest
126 |     ```
127 |     Ensure you use `:latest` or the specific version tag you intend to run.
128 | 
129 | ## Configuration
130 | 
131 | Create a `.env` file in the directory where you'll run the server, using `.env.example` as a template:
132 | 
133 | ```bash
134 | cp .env.example .env
135 | ```
136 | 
137 | Configure the following environment variables:
138 | 
139 | | Variable | Description | Example |
140 | |----------|-------------|---------|
141 | | `N8N_API_URL` | Full URL of the n8n API, including `/api/v1` | `http://localhost:5678/api/v1` |
142 | | `N8N_API_KEY` | API key for authenticating with n8n | `n8n_api_...` |
143 | | `N8N_WEBHOOK_USERNAME` | Username for webhook authentication (if using webhooks) | `username` |
144 | | `N8N_WEBHOOK_PASSWORD` | Password for webhook authentication | `password` |
145 | | `DEBUG` | Enable debug logging (optional) | `true` or `false` |
146 | 
147 | ### Generating an n8n API Key
148 | 
149 | 1. Open your n8n instance in a browser
150 | 2. Go to Settings > API > API Keys
151 | 3. Create a new API key with appropriate permissions
152 | 4. Copy the key to your `.env` file
153 | 
154 | ## Usage
155 | 
156 | ### Running the Server
157 | 
158 | From the installation directory:
159 | 
160 | ```bash
161 | n8n-mcp-server
162 | ```
163 | 
164 | Or if installed globally:
165 | 
166 | ```bash
167 | n8n-mcp-server
168 | ```
169 | 
170 | ### Integrating with AI Assistants
171 | 
172 | After building the server (`npm run build`), you need to configure your AI assistant (like VS Code with the Claude extension or the Claude Desktop app) to run it. This typically involves editing a JSON configuration file.
173 | 
174 | **Example Configuration (e.g., in VS Code `settings.json` or Claude Desktop `claude_desktop_config.json`):**
175 | 
176 | ```json
177 | {
178 |   "mcpServers": {
179 |     // Give your server a unique name
180 |     "n8n-local": {
181 |       // Use 'node' to execute the built JavaScript file
182 |       "command": "node",
183 |       // Provide the *absolute path* to the built index.js file
184 |       "args": [
185 |         "/path/to/your/cloned/n8n-mcp-server/build/index.js"
186 |         // On Windows, use double backslashes:
187 |         // "C:\\path\\to\\your\\cloned\\n8n-mcp-server\\build\\index.js"
188 |       ],
189 |       // Environment variables needed by the server
190 |       "env": {
191 |         "N8N_API_URL": "http://your-n8n-instance:5678/api/v1", // Replace with your n8n URL
192 |         "N8N_API_KEY": "YOUR_N8N_API_KEY", // Replace with your key
193 |         // Add webhook credentials only if you plan to use webhook tools
194 |         // "N8N_WEBHOOK_USERNAME": "your_webhook_user",
195 |         // "N8N_WEBHOOK_PASSWORD": "your_webhook_password"
196 |       },
197 |       // Ensure the server is enabled
198 |       "disabled": false,
199 |       // Default autoApprove settings
200 |       "autoApprove": []
201 |     }
202 |     // ... other servers might be configured here
203 |   }
204 | }
205 | ```
206 | 
207 | **Key Points:**
208 | 
209 | *   Replace `/path/to/your/cloned/n8n-mcp-server/` with the actual absolute path where you cloned and built the repository.
210 | *   Use the correct path separator for your operating system (forward slashes `/` for macOS/Linux, double backslashes `\\` for Windows).
211 | *   Ensure you provide the correct `N8N_API_URL` (including `/api/v1`) and `N8N_API_KEY`.
212 | *   The server needs to be built (`npm run build`) before the assistant can run the `build/index.js` file.
213 | 
214 | ## Available Tools
215 | 
216 | The server provides the following tools:
217 | 
218 | ### Using Webhooks
219 | 
220 | This MCP server supports executing workflows through n8n webhooks. To use this functionality:
221 | 
222 | 1. Create a webhook-triggered workflow in n8n.
223 | 2. Set up Basic Authentication on your webhook node.
224 | 3. Use the `run_webhook` tool to trigger the workflow, passing just the workflow name.
225 | 
226 | Example:
227 | ```javascript
228 | const result = await useRunWebhook({
229 |   workflowName: "hello-world", // Will call <n8n-url>/webhook/hello-world
230 |   data: {
231 |     prompt: "Hello from AI assistant!"
232 |   }
233 | });
234 | ```
235 | 
236 | The webhook authentication is handled automatically using the `N8N_WEBHOOK_USERNAME` and `N8N_WEBHOOK_PASSWORD` environment variables.
237 | 
238 | ### Workflow Management
239 | 
240 | - `workflow_list`: List all workflows
241 | - `workflow_get`: Get details of a specific workflow
242 | - `workflow_create`: Create a new workflow
243 | - `workflow_update`: Update an existing workflow
244 | - `workflow_delete`: Delete a workflow
245 | - `workflow_activate`: Activate a workflow
246 | - `workflow_deactivate`: Deactivate a workflow
247 | 
248 | ### Execution Management
249 | 
250 | - `execution_run`: Execute a workflow via the API
251 | - `run_webhook`: Execute a workflow via a webhook
252 | - `execution_get`: Get details of a specific execution
253 | - `execution_list`: List executions for a workflow
254 | - `execution_stop`: Stop a running execution
255 | 
256 | ## Resources
257 | 
258 | The server provides the following resources:
259 | 
260 | - `n8n://workflows/list`: List of all workflows
261 | - `n8n://workflow/{id}`: Details of a specific workflow
262 | - `n8n://executions/{workflowId}`: List of executions for a workflow
263 | - `n8n://execution/{id}`: Details of a specific execution
264 | 
265 | ## Roadmap
266 | 
267 | The n8n MCP Server is a community-driven project, and its future direction will be shaped by your feedback and contributions!
268 | 
269 | Currently, our roadmap is flexible and under continuous development. We believe in evolving the server based on the needs and ideas of our users.
270 | 
271 | We encourage you to get involved in shaping the future of this tool:
272 | 
273 | -   **Suggest Features:** Have an idea for a new tool, resource, or improvement?
274 | -   **Discuss Priorities:** Want to weigh in on what we should focus on next?
275 | 
276 | Please share your thoughts, feature requests, and ideas by opening an issue on our [GitHub Issues page](https://github.com/leonardsellem/n8n-mcp-server/issues). Let's build a powerful tool for AI assistants together!
277 | 
278 | ## Development
279 | 
280 | ### Building
281 | 
282 | ```bash
283 | npm run build
284 | ```
285 | 
286 | ### Running in Development Mode
287 | 
288 | ```bash
289 | npm run dev
290 | ```
291 | 
292 | ### Testing
293 | 
294 | ```bash
295 | npm test
296 | ```
297 | 
298 | ### Linting
299 | 
300 | ```bash
301 | npm run lint
302 | ```
303 | 
304 | ## Contributing
305 | 
306 | We welcome contributions from the community and are excited to see how you can help improve the n8n MCP Server! Whether you're fixing a bug, proposing a new feature, or improving documentation, your help is valued.
307 | 
308 | ### Reporting Bugs
309 | 
310 | If you encounter a bug, please report it by opening an issue on our [GitHub Issues page](https://github.com/leonardsellem/n8n-mcp-server/issues).
311 | 
312 | When submitting a bug report, please include the following:
313 | 
314 | -   A clear and descriptive title.
315 | -   A detailed description of the problem, including steps to reproduce the bug.
316 | -   Information about your environment (e.g., Node.js version, n8n MCP Server version, operating system).
317 | -   Any relevant error messages or screenshots.
318 | 
319 | ### Suggesting Enhancements
320 | 
321 | We're always looking for ways to make the server better. If you have an idea for an enhancement or a new feature, please open an issue on our [GitHub Issues page](https://github.com/leonardsellem/n8n-mcp-server/issues).
322 | 
323 | Please provide:
324 | 
325 | -   A clear and descriptive title for your suggestion.
326 | -   A detailed explanation of the proposed enhancement and why it would be beneficial.
327 | -   Any potential use cases or examples.
328 | 
329 | ### Submitting Pull Requests
330 | 
331 | If you'd like to contribute code, please follow these steps:
332 | 
333 | 1.  **Fork the repository:** Create your own fork of the [n8n-mcp-server repository](https://github.com/leonardsellem/n8n-mcp-server).
334 | 2.  **Create a branch:** Create a new branch in your fork for your changes (e.g., `git checkout -b feature/your-feature-name` or `bugfix/issue-number`).
335 | 3.  **Make your changes:** Implement your feature or bug fix.
336 |     *   Ensure your code adheres to the existing coding style. (We use Prettier for formatting, which can be run with `npm run lint`).
337 |     *   Include tests for your changes if applicable. You can run tests using `npm test`.
338 | 4.  **Commit your changes:** Write clear and concise commit messages.
339 | 5.  **Push to your fork:** Push your changes to your forked repository.
340 | 6.  **Open a Pull Request (PR):** Submit a PR to the `main` branch of the official `n8n-mcp-server` repository.
341 |     *   Provide a clear title and description for your PR, explaining the changes you've made and referencing any related issues.
342 | 
343 | We'll review your PR as soon as possible and provide feedback. Thank you for your contribution!
344 | 
345 | ## License
346 | 
347 | [MIT](LICENSE)
348 | 
349 | ## 🚀 Join Our Team: Call for Co-Maintainers!
350 | 
351 | This project is a vibrant, community-driven tool actively used by AI enthusiasts and developers. Currently, it's maintained on a part-time basis by a passionate individual who isn't a seasoned engineer but is dedicated to bridging AI with workflow automation. To help this project flourish, ensure its long-term health, and keep up with its growing user base, we're looking for enthusiastic **co-maintainers** to join the team!
352 | 
353 | ### Why Contribute?
354 | 
355 | -   **Learn and Grow:** Sharpen your skills in areas like TypeScript, Node.js, API integration, and AI tool development.
356 | -   **Collaborate:** Work alongside other motivated developers and AI users.
357 | -   **Make an Impact:** Directly shape the future of this project and help build a valuable tool for the AI community.
358 | -   **Open Source:** Gain experience contributing to an open-source project.
359 | 
360 | ### How You Can Help
361 | 
362 | We welcome contributions in many forms! Here are some areas where you could make a big difference:
363 | 
364 | -   **Bug Fixing:** Help us identify and squash bugs to improve stability.
365 | -   **Feature Development:** Implement new tools and functionalities based on user needs and your ideas.
366 | -   **Documentation:** Improve our guides, examples, and API references to make the project more accessible.
367 | -   **Testing:** Enhance our test suite (unit, integration) to ensure code quality and reliability.
368 | -   **CI/CD:** Help streamline our development and deployment pipelines.
369 | -   **Code Reviews:** Provide feedback on pull requests and help maintain code standards.
370 | -   **Community Support:** Assist users with questions and help manage discussions.
371 | 
372 | ### Get Involved!
373 | 
374 | If you're excited about the intersection of AI and workflow automation, and you're looking for a rewarding open-source opportunity, we'd love to hear from you!
375 | 
376 | **Ready to contribute?**
377 | 
378 | 1.  Check out our [GitHub Issues page](https://github.com/leonardsellem/n8n-mcp-server/issues) to find existing tasks, suggest new ideas, or express your interest in becoming a co-maintainer.
379 | 2.  You can open an issue titled "Co-maintainer Application" to formally apply, or simply start contributing to existing issues.
380 | 3.  Alternatively, feel free to reach out to the existing maintainers if you have questions.
381 | 
382 | Let’s build the future of AI-powered workflow automation together! 🙌
383 | 
384 | **Thanks to the community for the support!**
385 | [![Star History Chart](https://api.star-history.com/svg?repos=leonardsellem/n8n-mcp-server&type=Date)](https://www.star-history.com/#leonardsellem/n8n-mcp-server&Date)
386 | 
```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
 1 | # CLAUDE.md
 2 | 
 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
 4 | 
 5 | ## Project Status
 6 | 
 7 | This appears to be a new/empty repository for an n8n MCP (Model Context Protocol) server project.
 8 | 
 9 | ## Getting Started
10 | 
11 | When this project is initialized, common commands to look for:
12 | - `npm install` - Install dependencies
13 | - `npm run build` - Build the project
14 | - `npm run dev` - Run in development mode
15 | - `npm run test` - Run tests
16 | - `npm run lint` - Run linting
17 | 
18 | ## Architecture Notes
19 | 
20 | This will be populated once the project structure is established. Key areas to document:
21 | - MCP server implementation details
22 | - n8n integration patterns
23 | - Configuration management
24 | - API endpoints and protocols
```

--------------------------------------------------------------------------------
/AGENTS.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Guidelines for Coding Agents
 2 | 
 3 | These rules apply to all automated contributors working on this project.
 4 | 
 5 | ## Development
 6 | - Use **TypeScript** and ES modules. Source files live under `src/`.
 7 | - Place tests in the `tests/` directory mirroring the source structure. Test files must end with `.test.ts`.
 8 | - Install dependencies with `npm install` and build with `npm run build` when required.
 9 | - Format code using `npm run lint` and ensure all tests pass via `npm test` before committing.
10 | - Do **not** commit files ignored by `.gitignore` (e.g. `node_modules/`, `build/`, `.env`).
11 | - Follow existing patterns when adding new tools or resources as described in `docs/development`.
12 | 
13 | ## Commit Messages
14 | - Use short messages in the form `type: description` (e.g. `feat: add webhook tool`, `fix: handle null id`).
15 | 
16 | ## Pull Requests
17 | - Provide a concise summary of changes and reference related issues when opening a PR.
18 | - CI must pass before requesting review.
19 | 
20 | ## Environment
21 | - Node.js 20 or later is required.
22 | 
23 | ## Continuous Improvement
24 | - After completing a task, review this guide and update it with any lessons
25 |   learned about the codebase, coding principles, or user preferences.
26 | 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
1 | version: 1
2 | start:
3 |   command: ["node", "build/index.js"]
4 |   port: 8000 
```

--------------------------------------------------------------------------------
/tests/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": "../tsconfig.json",
 3 |   "compilerOptions": {
 4 |     "types": ["jest", "node"],
 5 |     "esModuleInterop": true,
 6 |     "rootDir": ".."
 7 |   },
 8 |   "include": [
 9 |     "**/*.ts",
10 |     "**/*.tsx"
11 |   ]
12 | }
13 | 
```

--------------------------------------------------------------------------------
/babel.config.cjs:
--------------------------------------------------------------------------------

```
1 | module.exports = {
2 |   presets: [
3 |     ['@babel/preset-env', { targets: { node: 'current' }, modules: false }],
4 |     '@babel/preset-typescript',
5 |   ],
6 |   plugins: [
7 |     // No explicit CJS transform plugin
8 |   ]
9 | };
```

--------------------------------------------------------------------------------
/src/errors/error-codes.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Error Codes Module
 3 |  * 
 4 |  * This module defines error codes used throughout the application.
 5 |  * These codes are compatible with the MCP SDK error handling system.
 6 |  */
 7 | 
 8 | // Numeric error codes for McpError
 9 | export enum ErrorCode {
10 |   InitializationError = 1000,
11 |   AuthenticationError = 1001,
12 |   NotFoundError = 1002,
13 |   InvalidRequest = 1003,
14 |   InternalError = 1004,
15 |   NotImplemented = 1005,
16 | }
17 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "outDir": "build",
 9 |     "declaration": true,
10 |     "sourceMap": true,
11 |     "resolveJsonModule": true,
12 |     "skipLibCheck": true,
13 |     "forceConsistentCasingInFileNames": true,
14 |     "rootDir": "src",
15 |     "lib": [
16 |       "ES2020",
17 |       "DOM"
18 |     ],
19 |     "types": [
20 |       "node"
21 |     ]
22 |   },
23 |   "include": [
24 |     "src/**/*"
25 |   ],
26 |   "exclude": [
27 |     "node_modules",
28 |     "build",
29 |     "**/*.test.ts"
30 |   ]
31 | }
32 | 
```

--------------------------------------------------------------------------------
/tests/test-setup.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Global test setup for n8n MCP Server tests
 3 |  */
 4 | import { beforeEach, afterEach, jest } from '@jest/globals';
 5 | 
 6 | // Reset environment variables before each test
 7 | beforeEach(() => {
 8 |   process.env = { 
 9 |     ...process.env,
10 |     NODE_ENV: 'test' 
11 |   };
12 | });
13 | 
14 | // Clean up after each test
15 | afterEach(() => {
16 |   jest.resetAllMocks();
17 |   jest.clearAllMocks();
18 | });
19 | 
20 | export const mockEnv = (envVars: Record<string, string>) => {
21 |   const originalEnv = process.env;
22 |   
23 |   beforeEach(() => {
24 |     process.env = { 
25 |       ...originalEnv,
26 |       ...envVars 
27 |     };
28 |   });
29 |   
30 |   afterEach(() => {
31 |     process.env = originalEnv;
32 |   });
33 | };
34 | 
```

--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------

```
 1 | astroid==3.3.8
 2 | certifi==2025.1.31
 3 | charset-normalizer==3.4.1
 4 | colorama==0.4.6
 5 | dill==0.3.9
 6 | filelock==3.17.0
 7 | fsspec==2025.2.0
 8 | huggingface-hub==0.29.2
 9 | idna==3.10
10 | isort==6.0.1
11 | Jinja2==3.1.6
12 | joblib==1.4.2
13 | MarkupSafe==3.0.2
14 | mccabe==0.7.0
15 | mpmath==1.3.0
16 | networkx==3.4.2
17 | numpy==2.2.3
18 | packaging==24.2
19 | pillow==11.1.0
20 | platformdirs==4.3.6
21 | pylint==3.3.4
22 | PyYAML==6.0.2
23 | regex==2024.11.6
24 | requests==2.32.3
25 | safetensors==0.5.3
26 | scikit-learn==1.6.1
27 | scipy==1.15.2
28 | sentence-transformers==3.4.1
29 | setuptools==75.8.2
30 | sympy==1.13.1
31 | threadpoolctl==3.5.0
32 | tokenizers==0.21.0
33 | tomlkit==0.13.2
34 | torch==2.6.0
35 | tqdm==4.67.1
36 | transformers==4.49.0
37 | typing_extensions==4.12.2
38 | urllib3==2.3.0
39 | 
```

--------------------------------------------------------------------------------
/docs/setup/index.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Setup and Configuration
 2 | 
 3 | This section covers everything you need to know to set up and configure the n8n MCP Server.
 4 | 
 5 | ## Topics
 6 | 
 7 | - [Installation](./installation.md): Instructions for installing the n8n MCP Server from npm or from source.
 8 | - [Configuration](./configuration.md): Information on configuring the server, including environment variables and n8n API setup.
 9 | - [Troubleshooting](./troubleshooting.md): Solutions to common issues you might encounter.
10 | 
11 | ## Quick Start
12 | 
13 | For a quick start, follow these steps:
14 | 
15 | 1. Install the server: `npm install -g @leonardsellem/n8n-mcp-server`
16 | 2. Create a `.env` file with your n8n API URL and API key
17 | 3. Run the server: `n8n-mcp-server`
18 | 4. Register the server with your AI assistant platform
19 | 
```

--------------------------------------------------------------------------------
/tests/unit/utils/resource-formatter.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Resource formatter utility tests
 3 |  */
 4 | 
 5 | import { describe, it, expect } from '@jest/globals';
 6 | import { formatResourceUri } from '../../../src/utils/resource-formatter.js';
 7 | 
 8 | describe('formatResourceUri', () => {
 9 |   it('appends "s" for singular resource types', () => {
10 |     expect(formatResourceUri('workflow', '1')).toBe('n8n://workflows/1');
11 |     expect(formatResourceUri('execution', '2')).toBe('n8n://executions/2');
12 |   });
13 | 
14 |   it('does not append "s" for already plural resource types', () => {
15 |     expect(formatResourceUri('workflows', '3')).toBe('n8n://workflows/3');
16 |     expect(formatResourceUri('execution-stats', '4')).toBe('n8n://execution-stats/4');
17 |   });
18 | 
19 |   it('returns URI without id when none is provided', () => {
20 |     expect(formatResourceUri('workflow')).toBe('n8n://workflow');
21 |     expect(formatResourceUri('workflows')).toBe('n8n://workflows');
22 |   });
23 | });
24 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Stage 1: Build the application
 2 | FROM node:20 AS builder
 3 | 
 4 | WORKDIR /app
 5 | 
 6 | # 1️⃣  Copy only dependency manifests first (keeps layer cache efficient)
 7 | COPY package*.json ./
 8 | 
 9 | # 2️⃣  Install deps *without* running any scripts ➜ skips the automatic `prepare`
10 | RUN npm ci --ignore-scripts            # dev + prod deps, no build yet
11 | 
12 | # 3️⃣  Now bring in the full source tree (tsconfig.json, src/, …)
13 | COPY . .
14 | 
15 | # 4️⃣  Build explicitly – everything is present now
16 | RUN npm run build
17 | 
18 | # 5️⃣  Strip dev-dependencies; keeps runtime small
19 | RUN npm prune --omit=dev
20 | 
21 | # Stage 2: Create the production image
22 | FROM node:20-slim
23 | 
24 | WORKDIR /app
25 | 
26 | # 6️⃣  Copy ready-to-run artefacts from builder
27 | COPY --from=builder /app/build ./build
28 | COPY --from=builder /app/node_modules ./node_modules
29 | COPY --from=builder /app/package*.json ./
30 | 
31 | # Set executable permissions for the binary
32 | RUN chmod +x build/index.js
33 | 
34 | # Expose the port the app runs on
35 | EXPOSE 8000
36 | 
37 | # Set the entrypoint to run the MCP server
38 | CMD ["node", "build/index.js"]
39 | 
```

--------------------------------------------------------------------------------
/src/tools/execution/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Execution Tools Module
 3 |  * 
 4 |  * This module provides MCP tools for interacting with n8n workflow executions.
 5 |  */
 6 | 
 7 | import { ToolDefinition } from '../../types/index.js';
 8 | import { getListExecutionsToolDefinition } from './list.js';
 9 | import { getGetExecutionToolDefinition } from './get.js';
10 | import { getDeleteExecutionToolDefinition } from './delete.js';
11 | import { getRunWebhookToolDefinition } from './run.js';
12 | 
13 | /**
14 |  * Set up execution management tools
15 |  * 
16 |  * @returns Array of execution tool definitions
17 |  */
18 | export async function setupExecutionTools(): Promise<ToolDefinition[]> {
19 |   return [
20 |     getListExecutionsToolDefinition(),
21 |     getGetExecutionToolDefinition(),
22 |     getDeleteExecutionToolDefinition(),
23 |     getRunWebhookToolDefinition()
24 |   ];
25 | }
26 | 
27 | // Export execution tool handlers for use in the handler
28 | export { ListExecutionsHandler } from './list.js';
29 | export { GetExecutionHandler } from './get.js';
30 | export { DeleteExecutionHandler } from './delete.js';
31 | export { RunWebhookHandler } from './run.js';
32 | 
```

--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Core Types Module
 3 |  * 
 4 |  * This module provides type definitions used throughout the application
 5 |  * and bridges compatibility with the MCP SDK.
 6 |  */
 7 | 
 8 | // Tool definition for MCP tools
 9 | export interface ToolDefinition {
10 |   name: string;
11 |   description: string;
12 |   inputSchema: {
13 |     type: string;
14 |     properties: Record<string, any>;
15 |     required?: string[];
16 |   };
17 | }
18 | 
19 | // Tool call result for MCP tool responses
20 | export interface ToolCallResult {
21 |   content: Array<{
22 |     type: string;
23 |     text: string;
24 |   }>;
25 |   isError?: boolean;
26 | }
27 | 
28 | // Type for n8n workflow object
29 | export interface Workflow {
30 |   id: string;
31 |   name: string;
32 |   active: boolean;
33 |   nodes: any[];
34 |   connections: any;
35 |   createdAt: string;
36 |   updatedAt: string;
37 |   [key: string]: any;
38 | }
39 | 
40 | // Type for n8n execution object
41 | export interface Execution {
42 |   id: string;
43 |   workflowId: string;
44 |   finished: boolean;
45 |   mode: string;
46 |   startedAt: string;
47 |   stoppedAt: string;
48 |   status: string;
49 |   data: {
50 |     resultData: {
51 |       runData: any;
52 |     };
53 |   };
54 |   [key: string]: any;
55 | }
56 | 
```

--------------------------------------------------------------------------------
/docs/examples/index.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Usage Examples
 2 | 
 3 | This section provides practical examples of using the n8n MCP Server with AI assistants.
 4 | 
 5 | ## Overview
 6 | 
 7 | The examples in this section demonstrate how AI assistants can interact with n8n workflows through the MCP server. They range from basic operations to complex integration scenarios.
 8 | 
 9 | ## Examples Categories
10 | 
11 | - [Basic Examples](./basic-examples.md): Simple examples covering fundamental operations like listing workflows, retrieving workflow details, and executing workflows.
12 | - [Advanced Scenarios](./advanced-scenarios.md): More complex examples showing how to chain operations, handle errors, and implement common workflow patterns.
13 | - [Integration Examples](./integration-examples.md): Examples of integrating the n8n MCP Server with different AI assistant platforms and other tools.
14 | 
15 | ## How to Use These Examples
16 | 
17 | The examples in this section show both:
18 | 
19 | 1. **User Prompts**: What a user might ask an AI assistant to do
20 | 2. **Assistant Actions**: How the assistant would use the MCP tools and resources to accomplish the task
21 | 
22 | You can use these examples as inspiration for your own interactions with the n8n MCP Server or as templates for building more complex workflows.
23 | 
```

--------------------------------------------------------------------------------
/run-tests.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * Test Runner Script
 3 |  * 
 4 |  * This script provides a more reliable way to run Jest tests with proper
 5 |  * ESM support and error handling.
 6 |  */
 7 | 
 8 | import { spawn } from 'child_process';
 9 | import { fileURLToPath } from 'url';
10 | import { dirname, resolve } from 'path';
11 | 
12 | // Get the directory of the current module
13 | const __filename = fileURLToPath(import.meta.url);
14 | const __dirname = dirname(__filename);
15 | 
16 | // Set NODE_OPTIONS to ensure proper ESM support
17 | process.env.NODE_OPTIONS = '--experimental-vm-modules';
18 | 
19 | console.log('🧪 Running tests for n8n MCP Server...');
20 | 
21 | // Get command line arguments to pass to Jest
22 | const args = process.argv.slice(2);
23 | const jestArgs = ['--config', './jest.config.cjs', ...args];
24 | 
25 | // Spawn Jest process
26 | const jestProcess = spawn('node_modules/.bin/jest', jestArgs, {
27 |   stdio: 'inherit',
28 |   cwd: __dirname,
29 |   env: { ...process.env, NODE_ENV: 'test' }
30 | });
31 | 
32 | // Handle process events
33 | jestProcess.on('error', (error) => {
34 |   console.error('Error running tests:', error);
35 |   process.exit(1);
36 | });
37 | 
38 | jestProcess.on('close', (code) => {
39 |   if (code !== 0) {
40 |     console.error(`Test process exited with code ${code}`);
41 |     process.exit(code);
42 |   }
43 |   console.log('✅ Tests completed successfully');
44 | });
45 | 
```

--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------

```markdown
 1 | # n8n MCP Server Documentation
 2 | 
 3 | Welcome to the n8n MCP Server documentation. This documentation provides comprehensive information about setting up, configuring, and using the n8n MCP Server.
 4 | 
 5 | ## Table of Contents
 6 | 
 7 | - [Setup and Configuration](./setup/index.md)
 8 |   - [Installation](./setup/installation.md)
 9 |   - [Configuration](./setup/configuration.md)
10 |   - [Troubleshooting](./setup/troubleshooting.md)
11 | 
12 | - [API Reference](./api/index.md)
13 |   - [Tools](./api/tools.md)
14 |     - [Workflow Tools](./api/workflow-tools.md)
15 |     - [Execution Tools](./api/execution-tools.md)
16 |   - [Resources](./api/resources.md)
17 |     - [Static Resources](./api/static-resources.md)
18 |     - [Dynamic Resources](./api/dynamic-resources.md)
19 | 
20 | - [Usage Examples](./examples/index.md)
21 |   - [Basic Examples](./examples/basic-examples.md)
22 |   - [Advanced Scenarios](./examples/advanced-scenarios.md)
23 |   - [Integration Examples](./examples/integration-examples.md)
24 | 
25 | - [Development](./development/index.md)
26 |   - [Architecture](./development/architecture.md)
27 |   - [Extending the Server](./development/extending.md)
28 |   - [Testing](./development/testing.md)
29 | 
30 | ## Quick Links
31 | 
32 | - [GitHub Repository](https://github.com/yourusername/n8n-mcp-server)
33 | - [n8n Documentation](https://docs.n8n.io/)
34 | - [Model Context Protocol Documentation](https://modelcontextprotocol.github.io/)
35 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | /**
 3 |  * n8n MCP Server - Main Entry Point
 4 |  * 
 5 |  * This file serves as the entry point for the n8n MCP Server,
 6 |  * which allows AI assistants to interact with n8n workflows through the MCP protocol.
 7 |  */
 8 | 
 9 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10 | import { loadEnvironmentVariables } from './config/environment.js';
11 | import { configureServer } from './config/server.js';
12 | 
13 | // Load environment variables
14 | loadEnvironmentVariables();
15 | 
16 | /**
17 |  * Main function to start the n8n MCP Server
18 |  */
19 | async function main() {
20 |   try {
21 |     console.error('Starting n8n MCP Server...');
22 | 
23 |     // Create and configure the MCP server
24 |     const server = await configureServer();
25 | 
26 |     // Set up error handling
27 |     server.onerror = (error: unknown) => console.error('[MCP Error]', error);
28 | 
29 |     // Set up clean shutdown
30 |     process.on('SIGINT', async () => {
31 |       console.error('Shutting down n8n MCP Server...');
32 |       await server.close();
33 |       process.exit(0);
34 |     });
35 | 
36 |     // Connect to the server transport (stdio)
37 |     const transport = new StdioServerTransport();
38 |     await server.connect(transport);
39 | 
40 |     console.error('n8n MCP Server running on stdio');
41 |   } catch (error) {
42 |     console.error('Failed to start n8n MCP Server:', error);
43 |     process.exit(1);
44 |   }
45 | }
46 | 
47 | // Start the server
48 | main().catch(console.error);
49 | 
```

--------------------------------------------------------------------------------
/tests/unit/resources/dynamic/workflow.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Simple test for URI Template functionality
 3 |  */
 4 | 
 5 | import { describe, it, expect } from '@jest/globals';
 6 | 
 7 | // Simple functions to test without complex imports
 8 | function getWorkflowResourceTemplateUri() {
 9 |   return 'n8n://workflows/{id}';
10 | }
11 | 
12 | function extractWorkflowIdFromUri(uri: string): string | null {
13 |   const regex = /^n8n:\/\/workflows\/([^/]+)$/;
14 |   const match = uri.match(regex);
15 |   return match ? match[1] : null;
16 | }
17 | 
18 | describe('Workflow Resource URI Functions', () => {
19 |   describe('getWorkflowResourceTemplateUri', () => {
20 |     it('should return the correct URI template', () => {
21 |       expect(getWorkflowResourceTemplateUri()).toBe('n8n://workflows/{id}');
22 |     });
23 |   });
24 |   
25 |   describe('extractWorkflowIdFromUri', () => {
26 |     it('should extract workflow ID from valid URI', () => {
27 |       expect(extractWorkflowIdFromUri('n8n://workflows/123abc')).toBe('123abc');
28 |       expect(extractWorkflowIdFromUri('n8n://workflows/workflow-name-with-dashes')).toBe('workflow-name-with-dashes');
29 |     });
30 |     
31 |     it('should return null for invalid URI formats', () => {
32 |       expect(extractWorkflowIdFromUri('n8n://workflows/')).toBeNull();
33 |       expect(extractWorkflowIdFromUri('n8n://workflows')).toBeNull();
34 |       expect(extractWorkflowIdFromUri('n8n://workflow/123')).toBeNull();
35 |       expect(extractWorkflowIdFromUri('invalid://workflows/123')).toBeNull();
36 |     });
37 |   });
38 | });
39 | 
```

--------------------------------------------------------------------------------
/.github/workflows/release-package.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Node.js Package
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |   pull_request:
 8 |     branches:
 9 |       - main
10 | 
11 | jobs:
12 |   build-and-publish:
13 |     runs-on: ubuntu-latest
14 |     permissions:
15 |       contents: write # Allow workflow to push to the repository
16 |     steps:
17 |       - uses: actions/checkout@v4
18 |         with:
19 |           token: ${{ github.token }} # Uses the default GITHUB_TOKEN
20 |       - uses: actions/setup-node@v4
21 |         with:
22 |           node-version: '20' # Specify your desired Node.js version
23 |           registry-url: 'https://registry.npmjs.org' # Point to npmjs.com
24 |       - run: npm ci
25 |       - run: npm run build # Add your build script here if you have one, otherwise remove this line
26 |       - name: Bump version, commit, and push
27 |         if: github.event_name == 'push' && github.ref == 'refs/heads/main'
28 |         run: |
29 |           git config --global user.name 'github-actions[bot]'
30 |           git config --global user.email 'github-actions[bot]@users.noreply.github.com'
31 |           npm version patch -m "chore: release %s"
32 |           git push
33 |           git push --tags
34 |         env:
35 |           NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} # NPM_TOKEN might be needed for npm version if it interacts with registry
36 |       - name: Publish to npmjs.com
37 |         if: github.event_name == 'push' && github.ref == 'refs/heads/main'
38 |         run: npm publish
39 |         env:
40 |           NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} # Use the NPM_TOKEN secret
41 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/get.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Get Workflow Tool
 3 |  * 
 4 |  * This tool retrieves a specific workflow from n8n by ID.
 5 |  */
 6 | 
 7 | import { BaseWorkflowToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { N8nApiError } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Handler for the get_workflow tool
13 |  */
14 | export class GetWorkflowHandler extends BaseWorkflowToolHandler {
15 |   /**
16 |    * Execute the tool
17 |    * 
18 |    * @param args Tool arguments containing workflowId
19 |    * @returns Workflow details
20 |    */
21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
22 |     return this.handleExecution(async (args) => {
23 |       const { workflowId } = args;
24 |       
25 |       if (!workflowId) {
26 |         throw new N8nApiError('Missing required parameter: workflowId');
27 |       }
28 |       
29 |       const workflow = await this.apiService.getWorkflow(workflowId);
30 |       
31 |       return this.formatSuccess(workflow, `Retrieved workflow: ${workflow.name}`);
32 |     }, args);
33 |   }
34 | }
35 | 
36 | /**
37 |  * Get tool definition for the get_workflow tool
38 |  * 
39 |  * @returns Tool definition
40 |  */
41 | export function getGetWorkflowToolDefinition(): ToolDefinition {
42 |   return {
43 |     name: 'get_workflow',
44 |     description: 'Retrieve a specific workflow by ID',
45 |     inputSchema: {
46 |       type: 'object',
47 |       properties: {
48 |         workflowId: {
49 |           type: 'string',
50 |           description: 'ID of the workflow to retrieve',
51 |         },
52 |       },
53 |       required: ['workflowId'],
54 |     },
55 |   };
56 | }
57 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Workflow Tools Module
 3 |  * 
 4 |  * This module provides MCP tools for interacting with n8n workflows.
 5 |  */
 6 | 
 7 | import { ToolDefinition } from '../../types/index.js';
 8 | 
 9 | // Import tool definitions
10 | import { getListWorkflowsToolDefinition, ListWorkflowsHandler } from './list.js';
11 | import { getGetWorkflowToolDefinition, GetWorkflowHandler } from './get.js';
12 | import { getCreateWorkflowToolDefinition, CreateWorkflowHandler } from './create.js';
13 | import { getUpdateWorkflowToolDefinition, UpdateWorkflowHandler } from './update.js';
14 | import { getDeleteWorkflowToolDefinition, DeleteWorkflowHandler } from './delete.js';
15 | import { getActivateWorkflowToolDefinition, ActivateWorkflowHandler } from './activate.js';
16 | import { getDeactivateWorkflowToolDefinition, DeactivateWorkflowHandler } from './deactivate.js';
17 | 
18 | // Export handlers
19 | export {
20 |   ListWorkflowsHandler,
21 |   GetWorkflowHandler,
22 |   CreateWorkflowHandler,
23 |   UpdateWorkflowHandler,
24 |   DeleteWorkflowHandler,
25 |   ActivateWorkflowHandler,
26 |   DeactivateWorkflowHandler,
27 | };
28 | 
29 | /**
30 |  * Set up workflow management tools
31 |  * 
32 |  * @returns Array of workflow tool definitions
33 |  */
34 | export async function setupWorkflowTools(): Promise<ToolDefinition[]> {
35 |   return [
36 |     getListWorkflowsToolDefinition(),
37 |     getGetWorkflowToolDefinition(),
38 |     getCreateWorkflowToolDefinition(),
39 |     getUpdateWorkflowToolDefinition(),
40 |     getDeleteWorkflowToolDefinition(),
41 |     getActivateWorkflowToolDefinition(),
42 |     getDeactivateWorkflowToolDefinition(),
43 |   ];
44 | }
45 | 
```

--------------------------------------------------------------------------------
/tests/unit/api/simple-client.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Simple HTTP client tests without complex dependencies
 3 |  */
 4 | 
 5 | import { describe, it, expect } from '@jest/globals';
 6 | 
 7 | // Create a simple HTTP client class to test
 8 | class SimpleHttpClient {
 9 |   constructor(private baseUrl: string, private apiKey: string) {}
10 |   
11 |   getBaseUrl(): string {
12 |     return this.baseUrl;
13 |   }
14 |   
15 |   getApiKey(): string {
16 |     return this.apiKey;
17 |   }
18 |   
19 |   buildAuthHeader(): Record<string, string> {
20 |     return {
21 |       'X-N8N-API-KEY': this.apiKey
22 |     };
23 |   }
24 |   
25 |   formatUrl(path: string): string {
26 |     return `${this.baseUrl}${path.startsWith('/') ? path : '/' + path}`;
27 |   }
28 | }
29 | 
30 | describe('SimpleHttpClient', () => {
31 |   it('should store baseUrl and apiKey properly', () => {
32 |     const baseUrl = 'https://n8n.example.com/api/v1';
33 |     const apiKey = 'test-api-key';
34 |     const client = new SimpleHttpClient(baseUrl, apiKey);
35 |     
36 |     expect(client.getBaseUrl()).toBe(baseUrl);
37 |     expect(client.getApiKey()).toBe(apiKey);
38 |   });
39 |   
40 |   it('should create proper auth headers', () => {
41 |     const client = new SimpleHttpClient('https://n8n.example.com/api/v1', 'test-api-key');
42 |     const headers = client.buildAuthHeader();
43 |     
44 |     expect(headers).toEqual({ 'X-N8N-API-KEY': 'test-api-key' });
45 |   });
46 |   
47 |   it('should format URLs correctly', () => {
48 |     const baseUrl = 'https://n8n.example.com/api/v1';
49 |     const client = new SimpleHttpClient(baseUrl, 'test-api-key');
50 |     
51 |     expect(client.formatUrl('workflows')).toBe(`${baseUrl}/workflows`);
52 |     expect(client.formatUrl('/workflows')).toBe(`${baseUrl}/workflows`);
53 |   });
54 | });
55 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/list.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * List Workflows Tool
 3 |  * 
 4 |  * This tool retrieves a list of workflows from n8n.
 5 |  */
 6 | 
 7 | import { BaseWorkflowToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition, Workflow } from '../../types/index.js';
 9 | 
10 | /**
11 |  * Handler for the list_workflows tool
12 |  */
13 | export class ListWorkflowsHandler extends BaseWorkflowToolHandler {
14 |   /**
15 |    * Execute the tool
16 |    * 
17 |    * @param args Tool arguments
18 |    * @returns List of workflows
19 |    */
20 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
21 |     return this.handleExecution(async () => {
22 |       const workflows = await this.apiService.getWorkflows();
23 |       
24 |       // Format the workflows for display
25 |       const formattedWorkflows = workflows.map((workflow: Workflow) => ({
26 |         id: workflow.id,
27 |         name: workflow.name,
28 |         active: workflow.active,
29 |         updatedAt: workflow.updatedAt,
30 |       }));
31 |       
32 |       return this.formatSuccess(
33 |         formattedWorkflows,
34 |         `Found ${formattedWorkflows.length} workflow(s)`
35 |       );
36 |     }, args);
37 |   }
38 | }
39 | 
40 | /**
41 |  * Get tool definition for the list_workflows tool
42 |  * 
43 |  * @returns Tool definition
44 |  */
45 | export function getListWorkflowsToolDefinition(): ToolDefinition {
46 |   return {
47 |     name: 'list_workflows',
48 |     description: 'Retrieve a list of all workflows available in n8n',
49 |     inputSchema: {
50 |       type: 'object',
51 |       properties: {
52 |         active: {
53 |           type: 'boolean',
54 |           description: 'Optional filter to show only active or inactive workflows',
55 |         },
56 |       },
57 |       required: [],
58 |     },
59 |   };
60 | }
61 | 
```

--------------------------------------------------------------------------------
/jest.config.cjs:
--------------------------------------------------------------------------------

```
 1 | module.exports = {
 2 |   // Use commonjs style export
 3 |   preset: 'ts-jest/presets/default-esm', // Using ESM preset for ts-jest
 4 |   testEnvironment: 'node',
 5 |   // transform: { // Removed to let ts-jest handle transformation via preset
 6 |   //   '^.+\\.tsx?$': 'babel-jest',
 7 |   // },
 8 |   // globals: { // Deprecated way to configure ts-jest
 9 |   //   'ts-jest': {
10 |   //     useESM: true,
11 |   //   }
12 |   // },
13 |   transform: {
14 |     '^.+\\.tsx?$': ['ts-jest', {
15 |       useESM: true,
16 |       tsconfig: 'tests/tsconfig.json', // Point to the tsconfig in the tests directory
17 |       // babelConfig: true, // If babel.config.cjs is still needed for some features not in ts-jest
18 |     }],
19 |   },
20 |   // Allow src and test folders to resolve imports properly
21 |   moduleNameMapper: {
22 |     '^(\\.{1,2}/.*)\\.js$': '$1',
23 |     // For ESM, Jest might need help resolving module paths if they are not fully specified
24 |     // or if there are conditions in package.json exports.
25 |     // Example: '(@modelcontextprotocol/sdk)(.*)': '<rootDir>/node_modules/$1/dist$2.js'
26 |     // This line below is a guess, might need adjustment or might not be needed.
27 |     // '^(@modelcontextprotocol/sdk/.*)\\.js$': '<rootDir>/node_modules/$1.js',
28 |   },
29 |   // Handle the modelcontextprotocol SDK
30 |   // Default is /node_modules/, so we want to NOT transform anything in node_modules
31 |   transformIgnorePatterns: [
32 |     "node_modules/"
33 |   ],
34 |   collectCoverage: true,
35 |   coverageDirectory: 'coverage',
36 |   coverageReporters: ['text', 'lcov'],
37 |   testMatch: ['**/tests/**/*.test.ts'],
38 |   verbose: true,
39 |   moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
40 |   setupFilesAfterEnv: ['<rootDir>/tests/test-setup.ts']
41 | };
42 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/activate.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Activate Workflow Tool
 3 |  * 
 4 |  * This tool activates an existing workflow in n8n.
 5 |  */
 6 | 
 7 | import { BaseWorkflowToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { N8nApiError } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Handler for the activate_workflow tool
13 |  */
14 | export class ActivateWorkflowHandler extends BaseWorkflowToolHandler {
15 |   /**
16 |    * Execute the tool
17 |    * 
18 |    * @param args Tool arguments containing workflowId
19 |    * @returns Activation confirmation
20 |    */
21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
22 |     return this.handleExecution(async (args) => {
23 |       const { workflowId } = args;
24 |       
25 |       if (!workflowId) {
26 |         throw new N8nApiError('Missing required parameter: workflowId');
27 |       }
28 |       
29 |       // Activate the workflow
30 |       const workflow = await this.apiService.activateWorkflow(workflowId);
31 |       
32 |       return this.formatSuccess(
33 |         {
34 |           id: workflow.id,
35 |           name: workflow.name,
36 |           active: workflow.active
37 |         },
38 |         `Workflow "${workflow.name}" (ID: ${workflowId}) has been successfully activated`
39 |       );
40 |     }, args);
41 |   }
42 | }
43 | 
44 | /**
45 |  * Get tool definition for the activate_workflow tool
46 |  * 
47 |  * @returns Tool definition
48 |  */
49 | export function getActivateWorkflowToolDefinition(): ToolDefinition {
50 |   return {
51 |     name: 'activate_workflow',
52 |     description: 'Activate a workflow in n8n',
53 |     inputSchema: {
54 |       type: 'object',
55 |       properties: {
56 |         workflowId: {
57 |           type: 'string',
58 |           description: 'ID of the workflow to activate',
59 |         },
60 |       },
61 |       required: ['workflowId'],
62 |     },
63 |   };
64 | }
65 | 
```

--------------------------------------------------------------------------------
/src/tools/execution/handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Execution Tools Handler
 3 |  * 
 4 |  * This module handles calls to execution-related tools.
 5 |  */
 6 | 
 7 | import { ToolCallResult } from '../../types/index.js';
 8 | import { McpError } from '@modelcontextprotocol/sdk/types.js';
 9 | import { ErrorCode } from '../../errors/error-codes.js';
10 | import { getErrorMessage } from '../../errors/index.js';
11 | import { 
12 |   ListExecutionsHandler, 
13 |   GetExecutionHandler,
14 |   DeleteExecutionHandler,
15 |   RunWebhookHandler
16 | } from './index.js';
17 | 
18 | /**
19 |  * Handle execution tool calls
20 |  * 
21 |  * @param toolName Name of the tool being called
22 |  * @param args Arguments passed to the tool
23 |  * @returns Tool call result
24 |  */
25 | export default async function executionHandler(
26 |   toolName: string,
27 |   args: Record<string, any>
28 | ): Promise<ToolCallResult> {
29 |   try {
30 |     // Route to the appropriate handler based on tool name
31 |     switch (toolName) {
32 |       case 'list_executions':
33 |         return await new ListExecutionsHandler().execute(args);
34 |         
35 |       case 'get_execution':
36 |         return await new GetExecutionHandler().execute(args);
37 |         
38 |       case 'delete_execution':
39 |         return await new DeleteExecutionHandler().execute(args);
40 |         
41 |       case 'run_webhook':
42 |         return await new RunWebhookHandler().execute(args);
43 |         
44 |       default:
45 |         throw new McpError(
46 |           ErrorCode.NotImplemented,
47 |           `Unknown execution tool: '${toolName}'`
48 |         );
49 |     }
50 |   } catch (error) {
51 |     // Get appropriate error message
52 |     const errorMessage = getErrorMessage(error);
53 |     
54 |     return {
55 |       content: [
56 |         {
57 |           type: 'text',
58 |           text: `Error executing execution tool: ${errorMessage}`,
59 |         },
60 |       ],
61 |       isError: true,
62 |     };
63 |   }
64 | }
65 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/deactivate.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Deactivate Workflow Tool
 3 |  * 
 4 |  * This tool deactivates an existing workflow in n8n.
 5 |  */
 6 | 
 7 | import { BaseWorkflowToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { N8nApiError } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Handler for the deactivate_workflow tool
13 |  */
14 | export class DeactivateWorkflowHandler extends BaseWorkflowToolHandler {
15 |   /**
16 |    * Execute the tool
17 |    * 
18 |    * @param args Tool arguments containing workflowId
19 |    * @returns Deactivation confirmation
20 |    */
21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
22 |     return this.handleExecution(async (args) => {
23 |       const { workflowId } = args;
24 |       
25 |       if (!workflowId) {
26 |         throw new N8nApiError('Missing required parameter: workflowId');
27 |       }
28 |       
29 |       // Deactivate the workflow
30 |       const workflow = await this.apiService.deactivateWorkflow(workflowId);
31 |       
32 |       return this.formatSuccess(
33 |         {
34 |           id: workflow.id,
35 |           name: workflow.name,
36 |           active: workflow.active
37 |         },
38 |         `Workflow "${workflow.name}" (ID: ${workflowId}) has been successfully deactivated`
39 |       );
40 |     }, args);
41 |   }
42 | }
43 | 
44 | /**
45 |  * Get tool definition for the deactivate_workflow tool
46 |  * 
47 |  * @returns Tool definition
48 |  */
49 | export function getDeactivateWorkflowToolDefinition(): ToolDefinition {
50 |   return {
51 |     name: 'deactivate_workflow',
52 |     description: 'Deactivate a workflow in n8n',
53 |     inputSchema: {
54 |       type: 'object',
55 |       properties: {
56 |         workflowId: {
57 |           type: 'string',
58 |           description: 'ID of the workflow to deactivate',
59 |         },
60 |       },
61 |       required: ['workflowId'],
62 |     },
63 |   };
64 | }
65 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/delete.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Delete Workflow Tool
 3 |  * 
 4 |  * This tool deletes an existing workflow from n8n.
 5 |  */
 6 | 
 7 | import { BaseWorkflowToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { N8nApiError } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Handler for the delete_workflow tool
13 |  */
14 | export class DeleteWorkflowHandler extends BaseWorkflowToolHandler {
15 |   /**
16 |    * Execute the tool
17 |    * 
18 |    * @param args Tool arguments containing workflowId
19 |    * @returns Deletion confirmation
20 |    */
21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
22 |     return this.handleExecution(async (args) => {
23 |       const { workflowId } = args;
24 |       
25 |       if (!workflowId) {
26 |         throw new N8nApiError('Missing required parameter: workflowId');
27 |       }
28 |       
29 |       // Get the workflow info first for the confirmation message
30 |       const workflow = await this.apiService.getWorkflow(workflowId);
31 |       const workflowName = workflow.name;
32 |       
33 |       // Delete the workflow
34 |       await this.apiService.deleteWorkflow(workflowId);
35 |       
36 |       return this.formatSuccess(
37 |         { id: workflowId },
38 |         `Workflow "${workflowName}" (ID: ${workflowId}) has been successfully deleted`
39 |       );
40 |     }, args);
41 |   }
42 | }
43 | 
44 | /**
45 |  * Get tool definition for the delete_workflow tool
46 |  * 
47 |  * @returns Tool definition
48 |  */
49 | export function getDeleteWorkflowToolDefinition(): ToolDefinition {
50 |   return {
51 |     name: 'delete_workflow',
52 |     description: 'Delete a workflow from n8n',
53 |     inputSchema: {
54 |       type: 'object',
55 |       properties: {
56 |         workflowId: {
57 |           type: 'string',
58 |           description: 'ID of the workflow to delete',
59 |         },
60 |       },
61 |       required: ['workflowId'],
62 |     },
63 |   };
64 | }
65 | 
```

--------------------------------------------------------------------------------
/docs/setup/installation.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Installation Guide
 2 | 
 3 | This guide covers the installation process for the n8n MCP Server.
 4 | 
 5 | ## Prerequisites
 6 | 
 7 | - Before installing the n8n MCP Server, ensure you have:
 8 | 
 9 | - Node.js 20 or later installed
10 | - An n8n instance running and accessible via HTTP/HTTPS
11 | - API access enabled on your n8n instance
12 | - An API key with appropriate permissions (see [Configuration](./configuration.md))
13 | 
14 | ## Option 1: Install from npm (Recommended)
15 | 
16 | The easiest way to install the n8n MCP Server is from npm:
17 | 
18 | ```bash
19 | npm install -g n8n-mcp-server
20 | ```
21 | 
22 | This will install the server globally, making the `n8n-mcp-server` command available in your terminal.
23 | 
24 | ## Option 2: Install from Source
25 | 
26 | For development purposes or to use the latest features, you can install from source:
27 | 
28 | ```bash
29 | # Clone the repository
30 | git clone https://github.com/yourusername/n8n-mcp-server.git
31 | cd n8n-mcp-server
32 | 
33 | # Install dependencies
34 | npm install
35 | 
36 | # Build the project
37 | npm run build
38 | 
39 | # Optional: Install globally
40 | npm install -g .
41 | ```
42 | 
43 | ## Verifying Installation
44 | 
45 | Once installed, you can verify the installation by running:
46 | 
47 | ```bash
48 | n8n-mcp-server --version
49 | ```
50 | 
51 | This should display the version number of the installed n8n MCP Server.
52 | 
53 | ## Next Steps
54 | 
55 | After installation, you'll need to:
56 | 
57 | 1. [Configure the server](./configuration.md) by setting up environment variables
58 | 2. Run the server
59 | 3. Register the server with your AI assistant platform
60 | 
61 | ## Upgrading
62 | 
63 | To upgrade a global installation from npm:
64 | 
65 | ```bash
66 | npm update -g n8n-mcp-server
67 | ```
68 | 
69 | To upgrade a source installation:
70 | 
71 | ```bash
72 | # Navigate to the repository directory
73 | cd n8n-mcp-server
74 | 
75 | # Pull the latest changes
76 | git pull
77 | 
78 | # Install dependencies and rebuild
79 | npm install
80 | npm run build
81 | 
82 | # If installed globally, reinstall
83 | npm install -g .
84 | 
```

--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Build & Publish Docker image
 2 | 
 3 | # ➊ Triggers – push to main OR new GitHub Release
 4 | on:
 5 |   push:
 6 |     branches: [main]
 7 |   release:
 8 |     types: [published]
 9 | 
10 | jobs:
11 |   build-and-push:
12 |     runs-on: ubuntu-latest
13 | 
14 |     # ➋ The job needs these repo secrets (add in Settings ▸ Secrets ▸ Actions):
15 |     #    DOCKERHUB_USERNAME  – e.g. 'leonardsellem'
16 |     #    DOCKERHUB_TOKEN     – a Docker Hub access token / password
17 |     env:
18 |       IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/n8n-mcp-server
19 | 
20 |     steps:
21 |       - name: Checkout repo
22 |         uses: actions/checkout@v4
23 | 
24 |       # ➌ Enable multi-arch builds (amd64 + arm64)
25 |       - uses: docker/setup-qemu-action@v3
26 |       - uses: docker/setup-buildx-action@v3
27 | 
28 |       # ➍ Log in to Docker Hub
29 |       - uses: docker/login-action@v3
30 |         with:
31 |           username: ${{ secrets.DOCKERHUB_USERNAME }}
32 |           password: ${{ secrets.DOCKERHUB_TOKEN }}
33 | 
34 |       # ➎ Generate convenient tags & labels (latest, sha-short, release tag …)
35 |       - id: meta
36 |         uses: docker/metadata-action@v5
37 |         with:
38 |           images: ${{ env.IMAGE_NAME }}
39 |           tags: |
40 |             type=sha,format=short
41 |             type=ref,event=branch
42 |             type=semver,pattern={{version}}
43 |             type=raw,value=latest,enable={{is_default_branch}}
44 | 
45 |       # ➏ Build & push
46 |       - uses: docker/build-push-action@v5
47 |         with:
48 |           context: .
49 |           push: true
50 |           platforms: linux/amd64,linux/arm64
51 |           tags: ${{ steps.meta.outputs.tags }}
52 |           labels: ${{ steps.meta.outputs.labels }}
53 |           # optional build-cache (makes repeated builds faster)
54 |           cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache
55 |           cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max
```

--------------------------------------------------------------------------------
/src/tools/execution/delete.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Delete Execution Tool
 3 |  * 
 4 |  * This tool deletes a specific workflow execution from n8n.
 5 |  */
 6 | 
 7 | import { BaseExecutionToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { McpError } from '@modelcontextprotocol/sdk/types.js';
10 | import { ErrorCode } from '../../errors/error-codes.js';
11 | 
12 | /**
13 |  * Handler for the delete_execution tool
14 |  */
15 | export class DeleteExecutionHandler extends BaseExecutionToolHandler {
16 |   /**
17 |    * Execute the tool
18 |    * 
19 |    * @param args Tool arguments (executionId)
20 |    * @returns Result of the deletion operation
21 |    */
22 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
23 |     return this.handleExecution(async () => {
24 |       // Validate required parameters
25 |       if (!args.executionId) {
26 |         throw new McpError(
27 |           ErrorCode.InvalidRequest,
28 |           'Missing required parameter: executionId'
29 |         );
30 |       }
31 |       
32 |       // Store execution ID for response message
33 |       const executionId = args.executionId;
34 |       
35 |       // Delete the execution
36 |       await this.apiService.deleteExecution(executionId);
37 |       
38 |       return this.formatSuccess(
39 |         { id: executionId, deleted: true },
40 |         `Successfully deleted execution with ID: ${executionId}`
41 |       );
42 |     }, args);
43 |   }
44 | }
45 | 
46 | /**
47 |  * Get tool definition for the delete_execution tool
48 |  * 
49 |  * @returns Tool definition
50 |  */
51 | export function getDeleteExecutionToolDefinition(): ToolDefinition {
52 |   return {
53 |     name: 'delete_execution',
54 |     description: 'Delete a specific workflow execution from n8n',
55 |     inputSchema: {
56 |       type: 'object',
57 |       properties: {
58 |         executionId: {
59 |           type: 'string',
60 |           description: 'ID of the execution to delete',
61 |         },
62 |       },
63 |       required: ['executionId'],
64 |     },
65 |   };
66 | }
67 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@leonardsellem/n8n-mcp-server",
 3 |   "version": "0.1.8",
 4 |   "description": "Model Context Protocol (MCP) server for n8n workflow automation",
 5 |   "main": "build/index.js",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc && chmod +x build/index.js",
 9 |     "start": "node build/index.js",
10 |     "dev": "tsc -w",
11 |     "lint": "eslint --ext .ts src/",
12 |     "test": "node --experimental-vm-modules run-tests.js",
13 |     "test:watch": "node --experimental-vm-modules run-tests.js --watch",
14 |     "test:coverage": "node --experimental-vm-modules run-tests.js --coverage",
15 |     "prepare": "npm run build"
16 |   },
17 |   "bin": {
18 |     "n8n-mcp-server": "build/index.js"
19 |   },
20 |   "keywords": [
21 |     "mcp",
22 |     "n8n",
23 |     "workflow",
24 |     "automation",
25 |     "ai"
26 |   ],
27 |   "author": "Leonard Sellem (https://sellem.me)",
28 |   "license": "MIT",
29 |   "repository": {
30 |     "type": "git",
31 |     "url": "git+https://github.com/leonardsellem/n8n-mcp-server.git"
32 |   },
33 |   "bugs": {
34 |     "url": "https://github.com/leonardsellem/n8n-mcp-server/issues"
35 |   },
36 |   "homepage": "https://github.com/leonardsellem/n8n-mcp-server#readme",
37 |   "files": [
38 |     "build",
39 |     "README.md",
40 |     "LICENSE",
41 |     "package.json"
42 |   ],
43 |   "dependencies": {
44 |     "@modelcontextprotocol/sdk": "^0.7.0",
45 |     "axios": "^1.6.2",
46 |     "dotenv": "^16.3.1",
47 |     "find-config": "^1.0.0"
48 |   },
49 |   "devDependencies": {
50 |     "@babel/core": "^7.26.10",
51 |     "@babel/plugin-transform-modules-commonjs": "^7.26.3",
52 |     "@babel/preset-env": "^7.26.9",
53 |     "@babel/preset-typescript": "^7.26.0",
54 |     "@types/find-config": "^1.0.4",
55 |     "@types/jest": "^29.5.14",
56 |     "@types/node": "^20.10.0",
57 |     "@typescript-eslint/eslint-plugin": "^6.13.1",
58 |     "@typescript-eslint/parser": "^6.13.1",
59 |     "babel-jest": "^29.7.0",
60 |     "eslint": "^8.54.0",
61 |     "jest": "^29.7.0",
62 |     "ts-jest": "^29.1.1",
63 |     "typescript": "^5.3.2"
64 |   },
65 |   "engines": {
66 |     "node": ">=18.0.0"
67 |   }
68 | }
69 | 
```

--------------------------------------------------------------------------------
/src/tools/execution/get.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Get Execution Tool
 3 |  * 
 4 |  * This tool retrieves detailed information about a specific workflow execution.
 5 |  */
 6 | 
 7 | import { BaseExecutionToolHandler } from './base-handler.js';
 8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
 9 | import { McpError } from '@modelcontextprotocol/sdk/types.js';
10 | import { ErrorCode } from '../../errors/error-codes.js';
11 | import { formatExecutionDetails } from '../../utils/execution-formatter.js';
12 | 
13 | /**
14 |  * Handler for the get_execution tool
15 |  */
16 | export class GetExecutionHandler extends BaseExecutionToolHandler {
17 |   /**
18 |    * Execute the tool
19 |    * 
20 |    * @param args Tool arguments (executionId)
21 |    * @returns Execution details
22 |    */
23 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
24 |     return this.handleExecution(async () => {
25 |       // Validate required parameters
26 |       if (!args.executionId) {
27 |         throw new McpError(
28 |           ErrorCode.InvalidRequest,
29 |           'Missing required parameter: executionId'
30 |         );
31 |       }
32 |       
33 |       // Get execution details
34 |       const execution = await this.apiService.getExecution(args.executionId);
35 |       
36 |       // Format the execution for display
37 |       const formattedExecution = formatExecutionDetails(execution);
38 |       
39 |       return this.formatSuccess(
40 |         formattedExecution,
41 |         `Execution Details for ID: ${args.executionId}`
42 |       );
43 |     }, args);
44 |   }
45 |   
46 | }
47 | 
48 | /**
49 |  * Get tool definition for the get_execution tool
50 |  * 
51 |  * @returns Tool definition
52 |  */
53 | export function getGetExecutionToolDefinition(): ToolDefinition {
54 |   return {
55 |     name: 'get_execution',
56 |     description: 'Retrieve detailed information about a specific workflow execution',
57 |     inputSchema: {
58 |       type: 'object',
59 |       properties: {
60 |         executionId: {
61 |           type: 'string',
62 |           description: 'ID of the execution to retrieve',
63 |         },
64 |       },
65 |       required: ['executionId'],
66 |     },
67 |   };
68 | }
69 | 
```

--------------------------------------------------------------------------------
/src/resources/static/execution-stats.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Static Execution Statistics Resource Handler
 3 |  * 
 4 |  * This module provides the MCP resource implementation for execution statistics.
 5 |  */
 6 | 
 7 | import { N8nApiService } from '../../api/n8n-client.js';
 8 | import { formatExecutionStats, formatResourceUri } from '../../utils/resource-formatter.js';
 9 | import { McpError, ErrorCode } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Get execution statistics resource data
13 |  * 
14 |  * @param apiService n8n API service
15 |  * @returns Formatted execution statistics resource data
16 |  */
17 | export async function getExecutionStatsResource(apiService: N8nApiService): Promise<string> {
18 |   try {
19 |     // Get executions from the API
20 |     const executions = await apiService.getExecutions();
21 |     
22 |     // Format the execution statistics
23 |     const stats = formatExecutionStats(executions);
24 |     
25 |     // Add metadata about the resource
26 |     const result = {
27 |       resourceType: 'execution-stats',
28 |       ...stats,
29 |       _links: {
30 |         self: formatResourceUri('execution-stats'),
31 |       }
32 |     };
33 |     
34 |     return JSON.stringify(result, null, 2);
35 |   } catch (error) {
36 |     console.error('Error fetching execution statistics resource:', error);
37 |     throw new McpError(
38 |       ErrorCode.InternalError, 
39 |       `Failed to retrieve execution statistics: ${error instanceof Error ? error.message : 'Unknown error'}`
40 |     );
41 |   }
42 | }
43 | 
44 | /**
45 |  * Get execution statistics resource URI
46 |  * 
47 |  * @returns Formatted resource URI
48 |  */
49 | export function getExecutionStatsResourceUri(): string {
50 |   return formatResourceUri('execution-stats');
51 | }
52 | 
53 | /**
54 |  * Get execution statistics resource metadata
55 |  * 
56 |  * @returns Resource metadata object
57 |  */
58 | export function getExecutionStatsResourceMetadata(): Record<string, any> {
59 |   return {
60 |     uri: getExecutionStatsResourceUri(),
61 |     name: 'n8n Execution Statistics',
62 |     mimeType: 'application/json',
63 |     description: 'Summary statistics of workflow executions including success rates, average duration, and trends',
64 |   };
65 | }
66 | 
```

--------------------------------------------------------------------------------
/src/resources/static/workflows.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Static Workflows Resource Handler
 3 |  * 
 4 |  * This module provides the MCP resource implementation for listing all workflows.
 5 |  */
 6 | 
 7 | import { N8nApiService } from '../../api/n8n-client.js';
 8 | import { formatWorkflowSummary, formatResourceUri } from '../../utils/resource-formatter.js';
 9 | import { McpError, ErrorCode } from '../../errors/index.js';
10 | 
11 | /**
12 |  * Get workflows resource data
13 |  * 
14 |  * @param apiService n8n API service
15 |  * @returns Formatted workflows resource data
16 |  */
17 | export async function getWorkflowsResource(apiService: N8nApiService): Promise<string> {
18 |   try {
19 |     // Get all workflows from the API
20 |     const workflows = await apiService.getWorkflows();
21 |     
22 |     // Format the workflows for resource consumption
23 |     const formattedWorkflows = workflows.map(workflow => formatWorkflowSummary(workflow));
24 |     
25 |     // Add metadata about the resource
26 |     const result = {
27 |       resourceType: 'workflows',
28 |       count: formattedWorkflows.length,
29 |       workflows: formattedWorkflows,
30 |       _links: {
31 |         self: formatResourceUri('workflows'),
32 |       },
33 |       lastUpdated: new Date().toISOString(),
34 |     };
35 |     
36 |     return JSON.stringify(result, null, 2);
37 |   } catch (error) {
38 |     console.error('Error fetching workflows resource:', error);
39 |     throw new McpError(
40 |       ErrorCode.InternalError, 
41 |       `Failed to retrieve workflows: ${error instanceof Error ? error.message : 'Unknown error'}`
42 |     );
43 |   }
44 | }
45 | 
46 | /**
47 |  * Get workflows resource URI
48 |  * 
49 |  * @returns Formatted resource URI
50 |  */
51 | export function getWorkflowsResourceUri(): string {
52 |   return formatResourceUri('workflows');
53 | }
54 | 
55 | /**
56 |  * Get workflows resource metadata
57 |  * 
58 |  * @returns Resource metadata object
59 |  */
60 | export function getWorkflowsResourceMetadata(): Record<string, any> {
61 |   return {
62 |     uri: getWorkflowsResourceUri(),
63 |     name: 'n8n Workflows',
64 |     mimeType: 'application/json',
65 |     description: 'List of all workflows in the n8n instance with their basic information',
66 |   };
67 | }
68 | 
```

--------------------------------------------------------------------------------
/tests/jest-globals.d.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Jest global type declarations
 3 |  * This file adds typings for Jest globals to reduce TypeScript errors in test files
 4 |  */
 5 | 
 6 | import '@jest/globals';
 7 | 
 8 | // Declare global Jest types explicitly to help TypeScript
 9 | declare global {
10 |   // Jest testing functions
11 |   const describe: typeof import('@jest/globals').describe;
12 |   const it: typeof import('@jest/globals').it;
13 |   const test: typeof import('@jest/globals').test;
14 |   const expect: typeof import('@jest/globals').expect;
15 |   const beforeAll: typeof import('@jest/globals').beforeAll;
16 |   const beforeEach: typeof import('@jest/globals').beforeEach;
17 |   const afterAll: typeof import('@jest/globals').afterAll;
18 |   const afterEach: typeof import('@jest/globals').afterEach;
19 |   
20 |   // Jest mock functionality
21 |   const jest: typeof import('@jest/globals').jest;
22 |   
23 |   // Additional common helpers
24 |   namespace jest {
25 |     interface Mock<T = any, Y extends any[] = any[]> extends Function {
26 |       new (...args: Y): T;
27 |       (...args: Y): T;
28 |       mockImplementation(fn: (...args: Y) => T): this;
29 |       mockImplementationOnce(fn: (...args: Y) => T): this;
30 |       mockReturnValue(value: T): this;
31 |       mockReturnValueOnce(value: T): this;
32 |       mockResolvedValue(value: T): this;
33 |       mockResolvedValueOnce(value: T): this;
34 |       mockRejectedValue(value: any): this;
35 |       mockRejectedValueOnce(value: any): this;
36 |       mockClear(): this;
37 |       mockReset(): this;
38 |       mockRestore(): this;
39 |       mockName(name: string): this;
40 |       getMockName(): string;
41 |       mock: {
42 |         calls: Y[];
43 |         instances: T[];
44 |         contexts: any[];
45 |         lastCall: Y;
46 |         results: Array<{ type: string; value: T }>;
47 |       };
48 |     }
49 |     
50 |     function fn<T = any, Y extends any[] = any[]>(): Mock<T, Y>;
51 |     function fn<T = any, Y extends any[] = any[]>(implementation: (...args: Y) => T): Mock<T, Y>;
52 |     
53 |     function spyOn<T extends object, M extends keyof T>(
54 |       object: T,
55 |       method: M & string
56 |     ): Mock<Required<T>[M]>;
57 |     
58 |     function mocked<T>(item: T, deep?: boolean): jest.Mocked<T>;
59 |   }
60 | }
61 | 
62 | export {};
63 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Claude Code
 2 | 
 3 | on:
 4 |   issue_comment:
 5 |     types: [created]
 6 |   pull_request_review_comment:
 7 |     types: [created]
 8 |   issues:
 9 |     types: [opened, assigned]
10 |   pull_request_review:
11 |     types: [submitted]
12 | 
13 | jobs:
14 |   claude:
15 |     if: |
16 |       (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17 |       (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18 |       (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19 |       (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20 |     runs-on: ubuntu-latest
21 |     permissions:
22 |       contents: read
23 |       pull-requests: read
24 |       issues: read
25 |       id-token: write
26 |     steps:
27 |       - name: Checkout repository
28 |         uses: actions/checkout@v4
29 |         with:
30 |           fetch-depth: 1
31 | 
32 |       - name: Run Claude Code
33 |         id: claude
34 |         uses: anthropics/claude-code-action@beta
35 |         with:
36 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
37 |           
38 |           # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
39 |           # model: "claude-opus-4-20250514"
40 |           
41 |           # Optional: Customize the trigger phrase (default: @claude)
42 |           # trigger_phrase: "/claude"
43 |           
44 |           # Optional: Trigger when specific user is assigned to an issue
45 |           # assignee_trigger: "claude-bot"
46 |           
47 |           # Optional: Allow Claude to run specific commands
48 |           # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)"
49 |           
50 |           # Optional: Add custom instructions for Claude to customize its behavior for your project
51 |           # custom_instructions: |
52 |           #   Follow our coding standards
53 |           #   Ensure all new code has tests
54 |           #   Use TypeScript for new files
55 |           
56 |           # Optional: Custom environment variables for Claude
57 |           # claude_env: |
58 |           #   NODE_ENV: test
59 | 
60 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Workflow Tools Handler
 3 |  * 
 4 |  * This module handles calls to workflow-related tools.
 5 |  */
 6 | 
 7 | import { ToolCallResult } from '../../types/index.js';
 8 | import { N8nApiError } from '../../errors/index.js';
 9 | import {
10 |   ListWorkflowsHandler,
11 |   GetWorkflowHandler,
12 |   CreateWorkflowHandler,
13 |   UpdateWorkflowHandler,
14 |   DeleteWorkflowHandler,
15 |   ActivateWorkflowHandler,
16 |   DeactivateWorkflowHandler,
17 | } from './index.js';
18 | 
19 | /**
20 |  * Handle workflow tool calls
21 |  * 
22 |  * @param toolName Name of the tool being called
23 |  * @param args Arguments passed to the tool
24 |  * @returns Tool call result
25 |  */
26 | export default async function workflowHandler(
27 |   toolName: string,
28 |   args: Record<string, any>
29 | ): Promise<ToolCallResult> {
30 |   try {
31 |     // Route to the appropriate handler based on the tool name
32 |     switch (toolName) {
33 |       case 'list_workflows':
34 |         return await new ListWorkflowsHandler().execute(args);
35 |         
36 |       case 'get_workflow':
37 |         return await new GetWorkflowHandler().execute(args);
38 |         
39 |       case 'create_workflow':
40 |         return await new CreateWorkflowHandler().execute(args);
41 |         
42 |       case 'update_workflow':
43 |         return await new UpdateWorkflowHandler().execute(args);
44 |         
45 |       case 'delete_workflow':
46 |         return await new DeleteWorkflowHandler().execute(args);
47 |         
48 |       case 'activate_workflow':
49 |         return await new ActivateWorkflowHandler().execute(args);
50 |         
51 |       case 'deactivate_workflow':
52 |         return await new DeactivateWorkflowHandler().execute(args);
53 |         
54 |       default:
55 |         throw new N8nApiError(`Unknown workflow tool: ${toolName}`);
56 |     }
57 |   } catch (error) {
58 |     if (error instanceof N8nApiError) {
59 |       return {
60 |         content: [
61 |           {
62 |             type: 'text',
63 |             text: error.message,
64 |           },
65 |         ],
66 |         isError: true,
67 |       };
68 |     }
69 |     
70 |     // Handle unexpected errors
71 |     const errorMessage = error instanceof Error 
72 |       ? error.message 
73 |       : 'Unknown error occurred';
74 |       
75 |     return {
76 |       content: [
77 |         {
78 |           type: 'text',
79 |           text: `Error executing workflow tool: ${errorMessage}`,
80 |         },
81 |       ],
82 |       isError: true,
83 |     };
84 |   }
85 | }
86 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/base-handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Base Workflow Tool Handler
 3 |  * 
 4 |  * This module provides a base handler for workflow-related tools.
 5 |  */
 6 | 
 7 | import { ToolCallResult } from '../../types/index.js';
 8 | import { N8nApiError } from '../../errors/index.js';
 9 | import { createApiService } from '../../api/n8n-client.js';
10 | import { getEnvConfig } from '../../config/environment.js';
11 | 
12 | /**
13 |  * Base class for workflow tool handlers
14 |  */
15 | export abstract class BaseWorkflowToolHandler {
16 |   protected apiService = createApiService(getEnvConfig());
17 |   
18 |   /**
19 |    * Validate and execute the tool
20 |    * 
21 |    * @param args Arguments passed to the tool
22 |    * @returns Tool call result
23 |    */
24 |   abstract execute(args: Record<string, any>): Promise<ToolCallResult>;
25 |   
26 |   /**
27 |    * Format a successful response
28 |    * 
29 |    * @param data Response data
30 |    * @param message Optional success message
31 |    * @returns Formatted success response
32 |    */
33 |   protected formatSuccess(data: any, message?: string): ToolCallResult {
34 |     const formattedData = typeof data === 'object' 
35 |       ? JSON.stringify(data, null, 2)
36 |       : String(data);
37 |       
38 |     return {
39 |       content: [
40 |         {
41 |           type: 'text',
42 |           text: message ? `${message}\n\n${formattedData}` : formattedData,
43 |         },
44 |       ],
45 |     };
46 |   }
47 |   
48 |   /**
49 |    * Format an error response
50 |    * 
51 |    * @param error Error object or message
52 |    * @returns Formatted error response
53 |    */
54 |   protected formatError(error: Error | string): ToolCallResult {
55 |     const errorMessage = error instanceof Error ? error.message : error;
56 |     
57 |     return {
58 |       content: [
59 |         {
60 |           type: 'text',
61 |           text: errorMessage,
62 |         },
63 |       ],
64 |       isError: true,
65 |     };
66 |   }
67 |   
68 |   /**
69 |    * Handle tool execution errors
70 |    * 
71 |    * @param handler Function to execute
72 |    * @param args Arguments to pass to the handler
73 |    * @returns Tool call result
74 |    */
75 |   protected async handleExecution(
76 |     handler: (args: Record<string, any>) => Promise<ToolCallResult>,
77 |     args: Record<string, any>
78 |   ): Promise<ToolCallResult> {
79 |     try {
80 |       return await handler(args);
81 |     } catch (error) {
82 |       if (error instanceof N8nApiError) {
83 |         return this.formatError(error.message);
84 |       }
85 |       
86 |       const errorMessage = error instanceof Error 
87 |         ? error.message 
88 |         : 'Unknown error occurred';
89 |         
90 |       return this.formatError(`Error executing workflow tool: ${errorMessage}`);
91 |     }
92 |   }
93 | }
94 | 
```

--------------------------------------------------------------------------------
/src/tools/execution/base-handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Base Execution Tool Handler
 3 |  * 
 4 |  * This module provides a base handler for execution-related tools.
 5 |  */
 6 | 
 7 | import { ToolCallResult } from '../../types/index.js';
 8 | import { N8nApiError } from '../../errors/index.js';
 9 | import { createApiService } from '../../api/n8n-client.js';
10 | import { getEnvConfig } from '../../config/environment.js';
11 | 
12 | /**
13 |  * Base class for execution tool handlers
14 |  */
15 | export abstract class BaseExecutionToolHandler {
16 |   protected apiService = createApiService(getEnvConfig());
17 |   
18 |   /**
19 |    * Validate and execute the tool
20 |    * 
21 |    * @param args Arguments passed to the tool
22 |    * @returns Tool call result
23 |    */
24 |   abstract execute(args: Record<string, any>): Promise<ToolCallResult>;
25 |   
26 |   /**
27 |    * Format a successful response
28 |    * 
29 |    * @param data Response data
30 |    * @param message Optional success message
31 |    * @returns Formatted success response
32 |    */
33 |   protected formatSuccess(data: any, message?: string): ToolCallResult {
34 |     const formattedData = typeof data === 'object' 
35 |       ? JSON.stringify(data, null, 2)
36 |       : String(data);
37 |       
38 |     return {
39 |       content: [
40 |         {
41 |           type: 'text',
42 |           text: message ? `${message}\n\n${formattedData}` : formattedData,
43 |         },
44 |       ],
45 |     };
46 |   }
47 |   
48 |   /**
49 |    * Format an error response
50 |    * 
51 |    * @param error Error object or message
52 |    * @returns Formatted error response
53 |    */
54 |   protected formatError(error: Error | string): ToolCallResult {
55 |     const errorMessage = error instanceof Error ? error.message : error;
56 |     
57 |     return {
58 |       content: [
59 |         {
60 |           type: 'text',
61 |           text: errorMessage,
62 |         },
63 |       ],
64 |       isError: true,
65 |     };
66 |   }
67 |   
68 |   /**
69 |    * Handle tool execution errors
70 |    * 
71 |    * @param handler Function to execute
72 |    * @param args Arguments to pass to the handler
73 |    * @returns Tool call result
74 |    */
75 |   protected async handleExecution(
76 |     handler: (args: Record<string, any>) => Promise<ToolCallResult>,
77 |     args: Record<string, any>
78 |   ): Promise<ToolCallResult> {
79 |     try {
80 |       return await handler(args);
81 |     } catch (error) {
82 |       if (error instanceof N8nApiError) {
83 |         return this.formatError(error.message);
84 |       }
85 |       
86 |       const errorMessage = error instanceof Error 
87 |         ? error.message 
88 |         : 'Unknown error occurred';
89 |         
90 |       return this.formatError(`Error executing execution tool: ${errorMessage}`);
91 |     }
92 |   }
93 | }
94 | 
```

--------------------------------------------------------------------------------
/tests/unit/config/simple-environment.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Simple environment configuration tests
 3 |  */
 4 | 
 5 | import { describe, it, expect } from '@jest/globals';
 6 | 
 7 | // Simple environment validation function to test
 8 | function validateEnvironment(env: Record<string, string | undefined>): { 
 9 |   n8nApiUrl: string;
10 |   n8nApiKey: string;
11 |   debug: boolean;
12 | } {
13 |   // Check required variables
14 |   if (!env.N8N_API_URL) {
15 |     throw new Error('Missing required environment variable: N8N_API_URL');
16 |   }
17 |   
18 |   if (!env.N8N_API_KEY) {
19 |     throw new Error('Missing required environment variable: N8N_API_KEY');
20 |   }
21 |   
22 |   // Validate URL format
23 |   try {
24 |     new URL(env.N8N_API_URL);
25 |   } catch (error) {
26 |     throw new Error(`Invalid URL format for N8N_API_URL: ${env.N8N_API_URL}`);
27 |   }
28 |   
29 |   // Return parsed config
30 |   return {
31 |     n8nApiUrl: env.N8N_API_URL,
32 |     n8nApiKey: env.N8N_API_KEY,
33 |     debug: env.DEBUG?.toLowerCase() === 'true'
34 |   };
35 | }
36 | 
37 | describe('Environment Configuration', () => {
38 |   describe('validateEnvironment', () => {
39 |     it('should return a valid config when all required variables are present', () => {
40 |       const env = {
41 |         N8N_API_URL: 'https://n8n.example.com/api/v1',
42 |         N8N_API_KEY: 'test-api-key'
43 |       };
44 |       
45 |       const config = validateEnvironment(env);
46 |       
47 |       expect(config).toEqual({
48 |         n8nApiUrl: 'https://n8n.example.com/api/v1',
49 |         n8nApiKey: 'test-api-key',
50 |         debug: false
51 |       });
52 |     });
53 |     
54 |     it('should set debug to true when DEBUG=true', () => {
55 |       const env = {
56 |         N8N_API_URL: 'https://n8n.example.com/api/v1',
57 |         N8N_API_KEY: 'test-api-key',
58 |         DEBUG: 'true'
59 |       };
60 |       
61 |       const config = validateEnvironment(env);
62 |       
63 |       expect(config.debug).toBe(true);
64 |     });
65 |     
66 |     it('should throw an error when N8N_API_URL is missing', () => {
67 |       const env = {
68 |         N8N_API_KEY: 'test-api-key'
69 |       };
70 |       
71 |       expect(() => validateEnvironment(env)).toThrow(
72 |         'Missing required environment variable: N8N_API_URL'
73 |       );
74 |     });
75 |     
76 |     it('should throw an error when N8N_API_KEY is missing', () => {
77 |       const env = {
78 |         N8N_API_URL: 'https://n8n.example.com/api/v1'
79 |       };
80 |       
81 |       expect(() => validateEnvironment(env)).toThrow(
82 |         'Missing required environment variable: N8N_API_KEY'
83 |       );
84 |     });
85 |     
86 |     it('should throw an error when N8N_API_URL is not a valid URL', () => {
87 |       const env = {
88 |         N8N_API_URL: 'invalid-url',
89 |         N8N_API_KEY: 'test-api-key'
90 |       };
91 |       
92 |       expect(() => validateEnvironment(env)).toThrow(
93 |         'Invalid URL format for N8N_API_URL: invalid-url'
94 |       );
95 |     });
96 |   });
97 | });
98 | 
```

--------------------------------------------------------------------------------
/docs/development/index.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Development Guide
 2 | 
 3 | This section provides information for developers who want to understand, maintain, or extend the n8n MCP Server.
 4 | 
 5 | ## Overview
 6 | 
 7 | The n8n MCP Server is built with TypeScript and implements the Model Context Protocol (MCP) to provide AI assistants with access to n8n workflows and executions. This development guide covers the architecture, extension points, and testing procedures.
 8 | 
 9 | ## Topics
10 | 
11 | - [Architecture](./architecture.md): Overview of the codebase organization and design patterns
12 | - [Extending the Server](./extending.md): Guide to adding new tools and resources
13 | - [Testing](./testing.md): Information on testing procedures and writing tests
14 | 
15 | ## Development Setup
16 | 
17 | To set up a development environment:
18 | 
19 | 1. Clone the repository:
20 |    ```bash
21 |    git clone https://github.com/yourusername/n8n-mcp-server.git
22 |    cd n8n-mcp-server
23 |    ```
24 | 
25 | 2. Install dependencies:
26 |    ```bash
27 |    npm install
28 |    ```
29 | 
30 | 3. Create a `.env` file for local development:
31 |    ```bash
32 |    cp .env.example .env
33 |    # Edit the .env file with your n8n API credentials
34 |    ```
35 | 
36 | 4. Start the development server:
37 |    ```bash
38 |    npm run dev
39 |    ```
40 | 
41 | This will compile the TypeScript code in watch mode, allowing you to make changes and see them take effect immediately.
42 | 
43 | ## Project Structure
44 | 
45 | The project follows a modular structure:
46 | 
47 | ```
48 | n8n-mcp-server/
49 | ├── src/                  # Source code
50 | │   ├── api/              # API client for n8n
51 | │   ├── config/           # Configuration and environment settings
52 | │   ├── errors/           # Error handling
53 | │   ├── resources/        # MCP resources implementation
54 | │   │   ├── static/       # Static resources
55 | │   │   └── dynamic/      # Dynamic (parameterized) resources
56 | │   ├── tools/            # MCP tools implementation
57 | │   │   ├── workflow/     # Workflow management tools
58 | │   │   └── execution/    # Execution management tools
59 | │   ├── types/            # TypeScript type definitions
60 | │   └── utils/            # Utility functions
61 | ├── tests/                # Test files
62 | │   ├── unit/             # Unit tests
63 | │   ├── integration/      # Integration tests
64 | │   └── e2e/              # End-to-end tests
65 | └── build/                # Compiled output
66 | ```
67 | 
68 | ## Build and Distribution
69 | 
70 | To build the project for distribution:
71 | 
72 | ```bash
73 | npm run build
74 | ```
75 | 
76 | This will compile the TypeScript code to JavaScript in the `build` directory and make the executable script file.
77 | 
78 | ## Development Workflow
79 | 
80 | 1. Create a feature branch for your changes
81 | 2. Make your changes and ensure tests pass
82 | 3. Update documentation as needed
83 | 4. Submit a pull request
84 | 
85 | For more detailed instructions on specific development tasks, see the linked guides.
86 | 
```

--------------------------------------------------------------------------------
/src/resources/dynamic/workflow.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Dynamic Workflow Resource Handler
 3 |  * 
 4 |  * This module provides the MCP resource implementation for retrieving
 5 |  * detailed workflow information by ID.
 6 |  */
 7 | 
 8 | import { N8nApiService } from '../../api/n8n-client.js';
 9 | import { formatWorkflowDetails, formatResourceUri } from '../../utils/resource-formatter.js';
10 | import { McpError, ErrorCode } from '../../errors/index.js';
11 | 
12 | /**
13 |  * Get workflow resource data by ID
14 |  * 
15 |  * @param apiService n8n API service
16 |  * @param workflowId Workflow ID
17 |  * @returns Formatted workflow resource data
18 |  */
19 | export async function getWorkflowResource(apiService: N8nApiService, workflowId: string): Promise<string> {
20 |   try {
21 |     // Get the specific workflow from the API
22 |     const workflow = await apiService.getWorkflow(workflowId);
23 |     
24 |     // Format the workflow for resource consumption
25 |     const formattedWorkflow = formatWorkflowDetails(workflow);
26 |     
27 |     // Add metadata about the resource
28 |     const result = {
29 |       resourceType: 'workflow',
30 |       id: workflowId,
31 |       ...formattedWorkflow,
32 |       _links: {
33 |         self: formatResourceUri('workflow', workflowId),
34 |         // Include links to related resources
35 |         executions: `n8n://executions?workflowId=${workflowId}`,
36 |       },
37 |       lastUpdated: new Date().toISOString(),
38 |     };
39 |     
40 |     return JSON.stringify(result, null, 2);
41 |   } catch (error) {
42 |     console.error(`Error fetching workflow resource (ID: ${workflowId}):`, error);
43 |     
44 |     // Handle not found errors specifically
45 |     if (error instanceof McpError && error.code === ErrorCode.NotFoundError) {
46 |       throw error;
47 |     }
48 |     
49 |     throw new McpError(
50 |       ErrorCode.InternalError, 
51 |       `Failed to retrieve workflow (ID: ${workflowId}): ${error instanceof Error ? error.message : 'Unknown error'}`
52 |     );
53 |   }
54 | }
55 | 
56 | /**
57 |  * Get workflow resource template URI
58 |  * 
59 |  * @returns Formatted resource template URI
60 |  */
61 | export function getWorkflowResourceTemplateUri(): string {
62 |   return 'n8n://workflows/{id}';
63 | }
64 | 
65 | /**
66 |  * Get workflow resource template metadata
67 |  * 
68 |  * @returns Resource template metadata object
69 |  */
70 | export function getWorkflowResourceTemplateMetadata(): Record<string, any> {
71 |   return {
72 |     uriTemplate: getWorkflowResourceTemplateUri(),
73 |     name: 'n8n Workflow Details',
74 |     mimeType: 'application/json',
75 |     description: 'Detailed information about a specific n8n workflow including all nodes, connections, and settings',
76 |   };
77 | }
78 | 
79 | /**
80 |  * Extract workflow ID from resource URI
81 |  * 
82 |  * @param uri Resource URI
83 |  * @returns Workflow ID or null if URI format is invalid
84 |  */
85 | export function extractWorkflowIdFromUri(uri: string): string | null {
86 |   const match = uri.match(/^n8n:\/\/workflows\/([^/]+)$/);
87 |   return match ? match[1] : null;
88 | }
89 | 
```

--------------------------------------------------------------------------------
/docs/api/index.md:
--------------------------------------------------------------------------------

```markdown
 1 | # API Reference
 2 | 
 3 | This section provides a comprehensive reference for the n8n MCP Server API, including all available tools and resources.
 4 | 
 5 | ## Overview
 6 | 
 7 | The n8n MCP Server implements the Model Context Protocol (MCP) to provide AI assistants with access to n8n workflows and executions. The API is divided into two main categories:
 8 | 
 9 | 1. **Tools**: Executable functions that can perform operations on n8n, such as creating workflows or starting executions.
10 | 2. **Resources**: Data sources that provide information about workflows and executions.
11 | 
12 | ## API Architecture
13 | 
14 | The n8n MCP Server follows a clean separation of concerns:
15 | 
16 | - **Client Layer**: Handles communication with the n8n API
17 | - **Transport Layer**: Implements the MCP protocol for communication with AI assistants
18 | - **Tools Layer**: Exposes executable operations to AI assistants
19 | - **Resources Layer**: Provides data access through URI-based resources
20 | 
21 | All API interactions are authenticated using the n8n API key configured in your environment.
22 | 
23 | ## Available Tools
24 | 
25 | The server provides tools for managing workflows and executions:
26 | 
27 | - [Workflow Tools](./workflow-tools.md): Create, list, update, and delete workflows
28 | - [Execution Tools](./execution-tools.md): Execute workflows and manage workflow executions
29 | 
30 | ## Available Resources
31 | 
32 | The server provides resources for accessing workflow and execution data:
33 | 
34 | - [Static Resources](./static-resources.md): Fixed resources like workflow listings or execution statistics
35 | - [Dynamic Resources](./dynamic-resources.md): Parameterized resources for specific workflows or executions
36 | 
37 | ## Understanding Input Schemas
38 | 
39 | Each tool has an input schema that defines the expected parameters. These schemas follow the JSON Schema format and are automatically provided to AI assistants to enable proper parameter validation and suggestion.
40 | 
41 | Example input schema for the `workflow_get` tool:
42 | 
43 | ```json
44 | {
45 |   "type": "object",
46 |   "properties": {
47 |     "id": {
48 |       "type": "string",
49 |       "description": "The ID of the workflow to retrieve"
50 |     }
51 |   },
52 |   "required": ["id"]
53 | }
54 | ```
55 | 
56 | ## Error Handling
57 | 
58 | All API operations can return errors in a standardized format. Common error scenarios include:
59 | 
60 | - Authentication failures (invalid or missing API key)
61 | - Resource not found (workflow or execution doesn't exist)
62 | - Permission issues (API key doesn't have required permissions)
63 | - Input validation errors (missing or invalid parameters)
64 | 
65 | Error responses include detailed messages to help troubleshoot issues.
66 | 
67 | ## Next Steps
68 | 
69 | Explore the detailed documentation for each category:
70 | 
71 | - [Workflow Tools](./workflow-tools.md)
72 | - [Execution Tools](./execution-tools.md)
73 | - [Static Resources](./static-resources.md)
74 | - [Dynamic Resources](./dynamic-resources.md)
75 | 
```

--------------------------------------------------------------------------------
/src/resources/dynamic/execution.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Dynamic Execution Resource Handler
 3 |  * 
 4 |  * This module provides the MCP resource implementation for retrieving
 5 |  * detailed execution information by ID.
 6 |  */
 7 | 
 8 | import { N8nApiService } from '../../api/n8n-client.js';
 9 | import { formatExecutionDetails } from '../../utils/execution-formatter.js';
10 | import { formatResourceUri } from '../../utils/resource-formatter.js';
11 | import { McpError, ErrorCode } from '../../errors/index.js';
12 | 
13 | /**
14 |  * Get execution resource data by ID
15 |  * 
16 |  * @param apiService n8n API service
17 |  * @param executionId Execution ID
18 |  * @returns Formatted execution resource data
19 |  */
20 | export async function getExecutionResource(apiService: N8nApiService, executionId: string): Promise<string> {
21 |   try {
22 |     // Get the specific execution from the API
23 |     const execution = await apiService.getExecution(executionId);
24 |     
25 |     // Format the execution for resource consumption
26 |     const formattedExecution = formatExecutionDetails(execution);
27 |     
28 |     // Add metadata about the resource
29 |     const result = {
30 |       resourceType: 'execution',
31 |       id: executionId,
32 |       ...formattedExecution,
33 |       _links: {
34 |         self: formatResourceUri('execution', executionId),
35 |         // Include link to related workflow
36 |         workflow: `n8n://workflows/${execution.workflowId}`,
37 |       },
38 |       lastUpdated: new Date().toISOString(),
39 |     };
40 |     
41 |     return JSON.stringify(result, null, 2);
42 |   } catch (error) {
43 |     console.error(`Error fetching execution resource (ID: ${executionId}):`, error);
44 |     
45 |     // Handle not found errors specifically
46 |     if (error instanceof McpError && error.code === ErrorCode.NotFoundError) {
47 |       throw error;
48 |     }
49 |     
50 |     throw new McpError(
51 |       ErrorCode.InternalError, 
52 |       `Failed to retrieve execution (ID: ${executionId}): ${error instanceof Error ? error.message : 'Unknown error'}`
53 |     );
54 |   }
55 | }
56 | 
57 | /**
58 |  * Get execution resource template URI
59 |  * 
60 |  * @returns Formatted resource template URI
61 |  */
62 | export function getExecutionResourceTemplateUri(): string {
63 |   return 'n8n://executions/{id}';
64 | }
65 | 
66 | /**
67 |  * Get execution resource template metadata
68 |  * 
69 |  * @returns Resource template metadata object
70 |  */
71 | export function getExecutionResourceTemplateMetadata(): Record<string, any> {
72 |   return {
73 |     uriTemplate: getExecutionResourceTemplateUri(),
74 |     name: 'n8n Execution Details',
75 |     mimeType: 'application/json',
76 |     description: 'Detailed information about a specific n8n workflow execution including node results and error information',
77 |   };
78 | }
79 | 
80 | /**
81 |  * Extract execution ID from resource URI
82 |  * 
83 |  * @param uri Resource URI
84 |  * @returns Execution ID or null if URI format is invalid
85 |  */
86 | export function extractExecutionIdFromUri(uri: string): string | null {
87 |   const match = uri.match(/^n8n:\/\/executions\/([^/]+)$/);
88 |   return match ? match[1] : null;
89 | }
90 | 
```

--------------------------------------------------------------------------------
/tests/unit/tools/workflow/simple-tool.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Simple workflow tool tests without complex dependencies
  3 |  */
  4 | 
  5 | import { describe, it, expect } from '@jest/globals';
  6 | 
  7 | interface MockWorkflow {
  8 |   id: string;
  9 |   name: string;
 10 |   active: boolean;
 11 |   createdAt: string;
 12 |   updatedAt: string;
 13 |   nodes: any[];
 14 | }
 15 | 
 16 | interface WorkflowFilter {
 17 |   active?: boolean;
 18 | }
 19 | 
 20 | // Mock workflow data
 21 | const mockWorkflows: MockWorkflow[] = [
 22 |   {
 23 |     id: '1234abc',
 24 |     name: 'Test Workflow 1',
 25 |     active: true,
 26 |     createdAt: '2025-03-01T12:00:00.000Z',
 27 |     updatedAt: '2025-03-02T14:30:00.000Z',
 28 |     nodes: []
 29 |   },
 30 |   {
 31 |     id: '5678def',
 32 |     name: 'Test Workflow 2',
 33 |     active: false,
 34 |     createdAt: '2025-03-01T12:00:00.000Z',
 35 |     updatedAt: '2025-03-12T10:15:00.000Z',
 36 |     nodes: []
 37 |   }
 38 | ];
 39 | 
 40 | // Simple function to test tool definition
 41 | function getListWorkflowsToolDefinition() {
 42 |   return {
 43 |     name: 'list_workflows',
 44 |     description: 'List all workflows with optional filtering by status',
 45 |     inputSchema: {
 46 |       type: 'object',
 47 |       properties: {
 48 |         active: {
 49 |           type: 'boolean',
 50 |           description: 'Filter workflows by active status'
 51 |         }
 52 |       },
 53 |       required: []
 54 |     }
 55 |   };
 56 | }
 57 | 
 58 | // Simple function to test workflow filtering
 59 | function filterWorkflows(workflows: MockWorkflow[], filter: WorkflowFilter): MockWorkflow[] {
 60 |   if (filter && typeof filter.active === 'boolean') {
 61 |     return workflows.filter(workflow => workflow.active === filter.active);
 62 |   }
 63 |   return workflows;
 64 | }
 65 | 
 66 | describe('Workflow Tools', () => {
 67 |   describe('getListWorkflowsToolDefinition', () => {
 68 |     it('should return the correct tool definition', () => {
 69 |       const definition = getListWorkflowsToolDefinition();
 70 |       
 71 |       expect(definition.name).toBe('list_workflows');
 72 |       expect(definition.description).toBeTruthy();
 73 |       expect(definition.inputSchema).toBeDefined();
 74 |       expect(definition.inputSchema.properties).toHaveProperty('active');
 75 |       expect(definition.inputSchema.required).toEqual([]);
 76 |     });
 77 |   });
 78 |   
 79 |   describe('filterWorkflows', () => {
 80 |     it('should return all workflows when no filter is provided', () => {
 81 |       const result = filterWorkflows(mockWorkflows, {});
 82 |       
 83 |       expect(result).toHaveLength(2);
 84 |       expect(result).toEqual(mockWorkflows);
 85 |     });
 86 |     
 87 |     it('should filter workflows by active status when active is true', () => {
 88 |       const result = filterWorkflows(mockWorkflows, { active: true });
 89 |       
 90 |       expect(result).toHaveLength(1);
 91 |       expect(result[0].id).toBe('1234abc');
 92 |       expect(result[0].active).toBe(true);
 93 |     });
 94 |     
 95 |     it('should filter workflows by active status when active is false', () => {
 96 |       const result = filterWorkflows(mockWorkflows, { active: false });
 97 |       
 98 |       expect(result).toHaveLength(1);
 99 |       expect(result[0].id).toBe('5678def');
100 |       expect(result[0].active).toBe(false);
101 |     });
102 |   });
103 | });
104 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude-code-review.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Claude Code Review
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     types: [opened, synchronize]
 6 |     # Optional: Only run on specific file changes
 7 |     # paths:
 8 |     #   - "src/**/*.ts"
 9 |     #   - "src/**/*.tsx"
10 |     #   - "src/**/*.js"
11 |     #   - "src/**/*.jsx"
12 | 
13 | jobs:
14 |   claude-review:
15 |     # Optional: Filter by PR author
16 |     # if: |
17 |     #   github.event.pull_request.user.login == 'external-contributor' ||
18 |     #   github.event.pull_request.user.login == 'new-developer' ||
19 |     #   github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20 |     
21 |     runs-on: ubuntu-latest
22 |     permissions:
23 |       contents: read
24 |       pull-requests: read
25 |       issues: read
26 |       id-token: write
27 |     
28 |     steps:
29 |       - name: Checkout repository
30 |         uses: actions/checkout@v4
31 |         with:
32 |           fetch-depth: 1
33 | 
34 |       - name: Run Claude Code Review
35 |         id: claude-review
36 |         uses: anthropics/claude-code-action@beta
37 |         with:
38 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
39 |           
40 |           # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
41 |           # model: "claude-opus-4-20250514"
42 |           
43 |           # Direct prompt for automated review (no @claude mention needed)
44 |           direct_prompt: |
45 |             Please review this pull request and provide feedback on:
46 |             - Code quality and best practices
47 |             - Potential bugs or issues
48 |             - Performance considerations
49 |             - Security concerns
50 |             - Test coverage
51 |             
52 |             Be constructive and helpful in your feedback.
53 |           
54 |           # Optional: Customize review based on file types
55 |           # direct_prompt: |
56 |           #   Review this PR focusing on:
57 |           #   - For TypeScript files: Type safety and proper interface usage
58 |           #   - For API endpoints: Security, input validation, and error handling
59 |           #   - For React components: Performance, accessibility, and best practices
60 |           #   - For tests: Coverage, edge cases, and test quality
61 |           
62 |           # Optional: Different prompts for different authors
63 |           # direct_prompt: |
64 |           #   ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && 
65 |           #   'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' ||
66 |           #   'Please provide a thorough code review focusing on our coding standards and best practices.' }}
67 |           
68 |           # Optional: Add specific tools for running tests or linting
69 |           # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)"
70 |           
71 |           # Optional: Skip review for certain conditions
72 |           # if: |
73 |           #   !contains(github.event.pull_request.title, '[skip-review]') &&
74 |           #   !contains(github.event.pull_request.title, '[WIP]')
75 | 
76 | 
```

--------------------------------------------------------------------------------
/src/config/environment.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Environment Configuration
  3 |  * 
  4 |  * This module handles loading and validating environment variables
  5 |  * required for connecting to the n8n API.
  6 |  */
  7 | 
  8 | import dotenv from 'dotenv';
  9 | import findConfig from 'find-config';
 10 | import path from 'path';
 11 | import { McpError } from '@modelcontextprotocol/sdk/types.js';
 12 | import { ErrorCode } from '../errors/error-codes.js';
 13 | 
 14 | // Environment variable names
 15 | export const ENV_VARS = {
 16 |   N8N_API_URL: 'N8N_API_URL',
 17 |   N8N_API_KEY: 'N8N_API_KEY',
 18 |   N8N_WEBHOOK_USERNAME: 'N8N_WEBHOOK_USERNAME',
 19 |   N8N_WEBHOOK_PASSWORD: 'N8N_WEBHOOK_PASSWORD',
 20 |   DEBUG: 'DEBUG',
 21 | };
 22 | 
 23 | // Interface for validated environment variables
 24 | export interface EnvConfig {
 25 |   n8nApiUrl: string;
 26 |   n8nApiKey: string;
 27 |   n8nWebhookUsername?: string; // Made optional
 28 |   n8nWebhookPassword?: string; // Made optional
 29 |   debug: boolean;
 30 | }
 31 | 
 32 | /**
 33 |  * Load environment variables from .env file if present
 34 |  */
 35 | export function loadEnvironmentVariables(): void {
 36 |   const {
 37 |     N8N_API_URL,
 38 |     N8N_API_KEY,
 39 |     N8N_WEBHOOK_USERNAME,
 40 |     N8N_WEBHOOK_PASSWORD
 41 |   } = process.env;
 42 | 
 43 |   if (
 44 |     !N8N_API_URL &&
 45 |     !N8N_API_KEY &&
 46 |     !N8N_WEBHOOK_USERNAME &&
 47 |     !N8N_WEBHOOK_PASSWORD
 48 |   ) {
 49 |     const projectRoot = findConfig('package.json');
 50 |     if (projectRoot) {
 51 |       const envPath = path.resolve(path.dirname(projectRoot), '.env');
 52 |       dotenv.config({ path: envPath });
 53 |     }
 54 |   }
 55 | }
 56 | 
 57 | /**
 58 |  * Validate and retrieve required environment variables
 59 |  * 
 60 |  * @returns Validated environment configuration
 61 |  * @throws {McpError} If required environment variables are missing
 62 |  */
 63 | export function getEnvConfig(): EnvConfig {
 64 |   const n8nApiUrl = process.env[ENV_VARS.N8N_API_URL];
 65 |   const n8nApiKey = process.env[ENV_VARS.N8N_API_KEY];
 66 |   const n8nWebhookUsername = process.env[ENV_VARS.N8N_WEBHOOK_USERNAME];
 67 |   const n8nWebhookPassword = process.env[ENV_VARS.N8N_WEBHOOK_PASSWORD];
 68 |   const debug = process.env[ENV_VARS.DEBUG]?.toLowerCase() === 'true';
 69 | 
 70 |   // Validate required core environment variables
 71 |   if (!n8nApiUrl) {
 72 |     throw new McpError(
 73 |       ErrorCode.InitializationError,
 74 |       `Missing required environment variable: ${ENV_VARS.N8N_API_URL}`
 75 |     );
 76 |   }
 77 | 
 78 |   if (!n8nApiKey) {
 79 |     throw new McpError(
 80 |       ErrorCode.InitializationError,
 81 |       `Missing required environment variable: ${ENV_VARS.N8N_API_KEY}`
 82 |     );
 83 |   }
 84 | 
 85 |   // N8N_WEBHOOK_USERNAME and N8N_WEBHOOK_PASSWORD are now optional at startup.
 86 |   // Tools requiring them should perform checks at the point of use.
 87 | 
 88 |   // Validate URL format
 89 |   try {
 90 |     new URL(n8nApiUrl);
 91 |   } catch (error) {
 92 |     throw new McpError(
 93 |       ErrorCode.InitializationError,
 94 |       `Invalid URL format for ${ENV_VARS.N8N_API_URL}: ${n8nApiUrl}`
 95 |     );
 96 |   }
 97 | 
 98 |   return {
 99 |     n8nApiUrl,
100 |     n8nApiKey,
101 |     n8nWebhookUsername: n8nWebhookUsername || undefined, // Ensure undefined if empty
102 |     n8nWebhookPassword: n8nWebhookPassword || undefined, // Ensure undefined if empty
103 |     debug,
104 |   };
105 | }
106 | 
```

--------------------------------------------------------------------------------
/tests/mocks/axios-mock.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Axios mock utilities for n8n MCP Server tests
  3 |  */
  4 | 
  5 | import { AxiosRequestConfig, AxiosResponse } from 'axios';
  6 | 
  7 | export interface MockResponse {
  8 |   data: any;
  9 |   status: number;
 10 |   statusText: string;
 11 |   headers?: Record<string, string>;
 12 |   config?: AxiosRequestConfig;
 13 | }
 14 | 
 15 | export const createMockAxiosResponse = (options: Partial<MockResponse> = {}): AxiosResponse => {
 16 |   return {
 17 |     data: options.data ?? {},
 18 |     status: options.status ?? 200,
 19 |     statusText: options.statusText ?? 'OK',
 20 |     headers: options.headers ?? {},
 21 |     config: options.config ?? {},
 22 |   } as AxiosResponse;
 23 | };
 24 | 
 25 | /**
 26 |  * Create a mock axios instance for testing
 27 |  */
 28 | export const createMockAxiosInstance = () => {
 29 |   const mockRequests: Record<string, any[]> = {};
 30 |   const mockResponses: Record<string, MockResponse[]> = {};
 31 |   
 32 |   const mockInstance = {
 33 |     get: jest.fn(),
 34 |     post: jest.fn(),
 35 |     put: jest.fn(),
 36 |     delete: jest.fn(),
 37 |     interceptors: {
 38 |       request: {
 39 |         use: jest.fn(),
 40 |       },
 41 |       response: {
 42 |         use: jest.fn(),
 43 |       },
 44 |     },
 45 |     defaults: {},
 46 |     
 47 |     // Helper method to add mock response
 48 |     addMockResponse(method: string, url: string, response: MockResponse | Error) {
 49 |       if (!mockResponses[`${method}:${url}`]) {
 50 |         mockResponses[`${method}:${url}`] = [];
 51 |       }
 52 |       
 53 |       if (response instanceof Error) {
 54 |         mockResponses[`${method}:${url}`].push(response as any);
 55 |       } else {
 56 |         mockResponses[`${method}:${url}`].push(response);
 57 |       }
 58 |     },
 59 |     
 60 |     // Helper method to get request history
 61 |     getRequestHistory(method: string, url: string) {
 62 |       return mockRequests[`${method}:${url}`] || [];
 63 |     },
 64 |     
 65 |     // Reset all mocks
 66 |     reset() {
 67 |       Object.keys(mockRequests).forEach(key => {
 68 |         delete mockRequests[key];
 69 |       });
 70 |       
 71 |       Object.keys(mockResponses).forEach(key => {
 72 |         delete mockResponses[key];
 73 |       });
 74 |       
 75 |       mockInstance.get.mockReset();
 76 |       mockInstance.post.mockReset();
 77 |       mockInstance.put.mockReset();
 78 |       mockInstance.delete.mockReset();
 79 |     }
 80 |   };
 81 |   
 82 |   // Setup method implementations
 83 |   ['get', 'post', 'put', 'delete'].forEach(method => {
 84 |     mockInstance[method].mockImplementation(async (url: string, data?: any) => {
 85 |       const requestKey = `${method}:${url}`;
 86 |       
 87 |       if (!mockRequests[requestKey]) {
 88 |         mockRequests[requestKey] = [];
 89 |       }
 90 |       
 91 |       mockRequests[requestKey].push(data);
 92 |       
 93 |       if (mockResponses[requestKey] && mockResponses[requestKey].length > 0) {
 94 |         const response = mockResponses[requestKey].shift();
 95 |         
 96 |         if (response instanceof Error) {
 97 |           throw response;
 98 |         }
 99 |         
100 |         return createMockAxiosResponse(response);
101 |       }
102 |       
103 |       throw new Error(`No mock response defined for ${method.toUpperCase()} ${url}`);
104 |     });
105 |   });
106 |   
107 |   return mockInstance;
108 | };
109 | 
110 | export default {
111 |   createMockAxiosResponse,
112 |   createMockAxiosInstance,
113 | };
114 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/create.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Create Workflow Tool
  3 |  * 
  4 |  * This tool creates a new workflow in n8n.
  5 |  */
  6 | 
  7 | import { BaseWorkflowToolHandler } from './base-handler.js';
  8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
  9 | import { N8nApiError } from '../../errors/index.js';
 10 | 
 11 | /**
 12 |  * Handler for the create_workflow tool
 13 |  */
 14 | export class CreateWorkflowHandler extends BaseWorkflowToolHandler {
 15 |   /**
 16 |    * Execute the tool
 17 |    * 
 18 |    * @param args Tool arguments containing workflow details
 19 |    * @returns Created workflow information
 20 |    */
 21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
 22 |     return this.handleExecution(async (args) => {
 23 |       const { name, nodes, connections, active, tags } = args;
 24 |       
 25 |       if (!name) {
 26 |         throw new N8nApiError('Missing required parameter: name');
 27 |       }
 28 |       
 29 |       // Validate nodes if provided
 30 |       if (nodes && !Array.isArray(nodes)) {
 31 |         throw new N8nApiError('Parameter "nodes" must be an array');
 32 |       }
 33 |       
 34 |       // Validate connections if provided
 35 |       if (connections && typeof connections !== 'object') {
 36 |         throw new N8nApiError('Parameter "connections" must be an object');
 37 |       }
 38 |       
 39 |       // Prepare workflow object
 40 |       const workflowData: Record<string, any> = {
 41 |         name,
 42 |         active: active === true,  // Default to false if not specified
 43 |       };
 44 |       
 45 |       // Add optional fields if provided
 46 |       if (nodes) workflowData.nodes = nodes;
 47 |       if (connections) workflowData.connections = connections;
 48 |       if (tags) workflowData.tags = tags;
 49 |       
 50 |       // Create the workflow
 51 |       const workflow = await this.apiService.createWorkflow(workflowData);
 52 |       
 53 |       return this.formatSuccess(
 54 |         {
 55 |           id: workflow.id,
 56 |           name: workflow.name,
 57 |           active: workflow.active
 58 |         },
 59 |         `Workflow created successfully`
 60 |       );
 61 |     }, args);
 62 |   }
 63 | }
 64 | 
 65 | /**
 66 |  * Get tool definition for the create_workflow tool
 67 |  * 
 68 |  * @returns Tool definition
 69 |  */
 70 | export function getCreateWorkflowToolDefinition(): ToolDefinition {
 71 |   return {
 72 |     name: 'create_workflow',
 73 |     description: 'Create a new workflow in n8n',
 74 |     inputSchema: {
 75 |       type: 'object',
 76 |       properties: {
 77 |         name: {
 78 |           type: 'string',
 79 |           description: 'Name of the workflow',
 80 |         },
 81 |         nodes: {
 82 |           type: 'array',
 83 |           description: 'Array of node objects that define the workflow',
 84 |           items: {
 85 |             type: 'object',
 86 |           },
 87 |         },
 88 |         connections: {
 89 |           type: 'object',
 90 |           description: 'Connection mappings between nodes',
 91 |         },
 92 |         active: {
 93 |           type: 'boolean',
 94 |           description: 'Whether the workflow should be active upon creation',
 95 |         },
 96 |         tags: {
 97 |           type: 'array',
 98 |           description: 'Tags to associate with the workflow',
 99 |           items: {
100 |             type: 'string',
101 |           },
102 |         },
103 |       },
104 |       required: ['name'],
105 |     },
106 |   };
107 | }
108 | 
```

--------------------------------------------------------------------------------
/src/tools/execution/list.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * List Executions Tool
  3 |  * 
  4 |  * This tool retrieves a list of workflow executions from n8n.
  5 |  */
  6 | 
  7 | import { BaseExecutionToolHandler } from './base-handler.js';
  8 | import { ToolCallResult, ToolDefinition, Execution } from '../../types/index.js';
  9 | import { formatExecutionSummary, summarizeExecutions } from '../../utils/execution-formatter.js';
 10 | 
 11 | /**
 12 |  * Handler for the list_executions tool
 13 |  */
 14 | export class ListExecutionsHandler extends BaseExecutionToolHandler {
 15 |   /**
 16 |    * Execute the tool
 17 |    * 
 18 |    * @param args Tool arguments (workflowId, status, limit, lastId)
 19 |    * @returns List of executions
 20 |    */
 21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
 22 |     return this.handleExecution(async () => {
 23 |       const executions = await this.apiService.getExecutions();
 24 |       
 25 |       // Apply filters if provided
 26 |       let filteredExecutions = executions;
 27 |       
 28 |       // Filter by workflow ID if provided
 29 |       if (args.workflowId) {
 30 |         filteredExecutions = filteredExecutions.filter(
 31 |           (execution: Execution) => execution.workflowId === args.workflowId
 32 |         );
 33 |       }
 34 |       
 35 |       // Filter by status if provided
 36 |       if (args.status) {
 37 |         filteredExecutions = filteredExecutions.filter(
 38 |           (execution: Execution) => execution.status === args.status
 39 |         );
 40 |       }
 41 |       
 42 |       // Apply limit if provided
 43 |       const limit = args.limit && args.limit > 0 ? args.limit : filteredExecutions.length;
 44 |       filteredExecutions = filteredExecutions.slice(0, limit);
 45 |       
 46 |       // Format the executions for display
 47 |       const formattedExecutions = filteredExecutions.map((execution: Execution) => 
 48 |         formatExecutionSummary(execution)
 49 |       );
 50 |       
 51 |       // Generate summary if requested
 52 |       let summary = undefined;
 53 |       if (args.includeSummary) {
 54 |         summary = summarizeExecutions(executions);
 55 |       }
 56 |       
 57 |       // Prepare response data
 58 |       const responseData = {
 59 |         executions: formattedExecutions,
 60 |         summary: summary,
 61 |         total: formattedExecutions.length,
 62 |         filtered: args.workflowId || args.status ? true : false
 63 |       };
 64 |       
 65 |       return this.formatSuccess(
 66 |         responseData,
 67 |         `Found ${formattedExecutions.length} execution(s)`
 68 |       );
 69 |     }, args);
 70 |   }
 71 | }
 72 | 
 73 | /**
 74 |  * Get tool definition for the list_executions tool
 75 |  * 
 76 |  * @returns Tool definition
 77 |  */
 78 | export function getListExecutionsToolDefinition(): ToolDefinition {
 79 |   return {
 80 |     name: 'list_executions',
 81 |     description: 'Retrieve a list of workflow executions from n8n',
 82 |     inputSchema: {
 83 |       type: 'object',
 84 |       properties: {
 85 |         workflowId: {
 86 |           type: 'string',
 87 |           description: 'Optional ID of workflow to filter executions by',
 88 |         },
 89 |         status: {
 90 |           type: 'string',
 91 |           description: 'Optional status to filter by (success, error, waiting, or canceled)',
 92 |         },
 93 |         limit: {
 94 |           type: 'number',
 95 |           description: 'Maximum number of executions to return',
 96 |         },
 97 |         lastId: {
 98 |           type: 'string',
 99 |           description: 'ID of the last execution for pagination',
100 |         },
101 |         includeSummary: {
102 |           type: 'boolean',
103 |           description: 'Include summary statistics about executions',
104 |         },
105 |       },
106 |       required: [],
107 |     },
108 |   };
109 | }
110 | 
```

--------------------------------------------------------------------------------
/src/errors/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Error Handling Module
  3 |  * 
  4 |  * This module provides custom error classes and error handling utilities
  5 |  * for the n8n MCP Server.
  6 |  */
  7 | 
  8 | import { McpError as SdkMcpError } from '@modelcontextprotocol/sdk/types.js';
  9 | import { ErrorCode } from './error-codes.js';
 10 | 
 11 | // Re-export McpError from SDK
 12 | export { McpError } from '@modelcontextprotocol/sdk/types.js';
 13 | // Re-export ErrorCode enum
 14 | export { ErrorCode } from './error-codes.js';
 15 | 
 16 | /**
 17 |  * n8n API Error class for handling errors from the n8n API
 18 |  */
 19 | export class N8nApiError extends SdkMcpError {
 20 |   constructor(message: string, statusCode?: number, details?: unknown) {
 21 |     // Map HTTP status codes to appropriate MCP error codes
 22 |     let errorCode = ErrorCode.InternalError;
 23 |     
 24 |     if (statusCode) {
 25 |       if (statusCode === 401 || statusCode === 403) {
 26 |         errorCode = ErrorCode.AuthenticationError;
 27 |       } else if (statusCode === 404) {
 28 |         errorCode = ErrorCode.NotFoundError;
 29 |       } else if (statusCode >= 400 && statusCode < 500) {
 30 |         errorCode = ErrorCode.InvalidRequest;
 31 |       }
 32 |     }
 33 |     
 34 |     super(errorCode, formatErrorMessage(message, statusCode, details));
 35 |   }
 36 | }
 37 | 
 38 | /**
 39 |  * Format an error message with status code and details
 40 |  */
 41 | function formatErrorMessage(message: string, statusCode?: number, details?: unknown): string {
 42 |   let formattedMessage = message;
 43 |   
 44 |   if (statusCode) {
 45 |     formattedMessage += ` (Status: ${statusCode})`;
 46 |   }
 47 |   
 48 |   if (details) {
 49 |     try {
 50 |       const detailsStr = typeof details === 'string' 
 51 |         ? details 
 52 |         : JSON.stringify(details, null, 2);
 53 |       formattedMessage += `\nDetails: ${detailsStr}`;
 54 |     } catch (error) {
 55 |       // Ignore JSON stringification errors
 56 |     }
 57 |   }
 58 |   
 59 |   return formattedMessage;
 60 | }
 61 | 
 62 | /**
 63 |  * Safely parse JSON response from n8n API
 64 |  * 
 65 |  * @param text Text to parse as JSON
 66 |  * @returns Parsed JSON object or null if parsing fails
 67 |  */
 68 | export function safeJsonParse(text: string): any {
 69 |   try {
 70 |     return JSON.parse(text);
 71 |   } catch (error) {
 72 |     return null;
 73 |   }
 74 | }
 75 | 
 76 | /**
 77 |  * Handle axios errors and convert them to N8nApiError
 78 |  * 
 79 |  * @param error Error object from axios
 80 |  * @param defaultMessage Default error message
 81 |  * @returns N8nApiError with appropriate details
 82 |  */
 83 | export function handleAxiosError(error: any, defaultMessage = 'n8n API request failed'): N8nApiError {
 84 |   // Handle axios error responses
 85 |   if (error.response) {
 86 |     const statusCode = error.response.status;
 87 |     const responseData = error.response.data;
 88 |     
 89 |     let errorMessage = defaultMessage;
 90 |     if (responseData && responseData.message) {
 91 |       errorMessage = responseData.message;
 92 |     }
 93 |     
 94 |     return new N8nApiError(errorMessage, statusCode, responseData);
 95 |   }
 96 |   
 97 |   // Handle request errors (e.g., network issues)
 98 |   if (error.request) {
 99 |     return new N8nApiError(
100 |       'Network error connecting to n8n API', 
101 |       undefined, 
102 |       error.message
103 |     );
104 |   }
105 |   
106 |   // Handle other errors
107 |   return new N8nApiError(error.message || defaultMessage);
108 | }
109 | 
110 | /**
111 |  * Extract a readable error message from an error object
112 |  * 
113 |  * @param error Error object
114 |  * @returns Readable error message
115 |  */
116 | export function getErrorMessage(error: unknown): string {
117 |   if (error instanceof Error) {
118 |     return error.message;
119 |   }
120 |   
121 |   if (typeof error === 'string') {
122 |     return error;
123 |   }
124 |   
125 |   try {
126 |     return JSON.stringify(error);
127 |   } catch {
128 |     return 'Unknown error';
129 |   }
130 | }
131 | 
```

--------------------------------------------------------------------------------
/docs/setup/configuration.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Configuration Guide
  2 | 
  3 | This guide provides detailed information on configuring the n8n MCP Server.
  4 | 
  5 | ## Environment Variables
  6 | 
  7 | The n8n MCP Server is configured using environment variables, which can be set in a `.env` file or directly in your environment.
  8 | 
  9 | ### Required Variables
 10 | 
 11 | | Variable | Description | Example |
 12 | |----------|-------------|---------|
 13 | | `N8N_API_URL` | URL of the n8n API | `http://localhost:5678/api/v1` |
 14 | | `N8N_API_KEY` | API key for authenticating with n8n | `n8n_api_...` |
 15 | 
 16 | ### Optional Variables
 17 | 
 18 | | Variable | Description | Default | Example |
 19 | |----------|-------------|---------|---------|
 20 | | `DEBUG` | Enable debug logging | `false` | `true` or `false` |
 21 | 
 22 | ## Creating a .env File
 23 | 
 24 | The simplest way to configure the server is to create a `.env` file in the directory where you'll run the server:
 25 | 
 26 | ```bash
 27 | # Copy the example .env file
 28 | cp .env.example .env
 29 | 
 30 | # Edit the .env file with your settings
 31 | nano .env  # or use any text editor
 32 | ```
 33 | 
 34 | Example `.env` file:
 35 | 
 36 | ```env
 37 | # n8n MCP Server Environment Variables
 38 | 
 39 | # Required: URL of the n8n API
 40 | N8N_API_URL=http://localhost:5678/api/v1
 41 | 
 42 | # Required: API key for authenticating with n8n
 43 | N8N_API_KEY=your_n8n_api_key_here
 44 | 
 45 | # Optional: Set to 'true' to enable debug logging
 46 | DEBUG=false
 47 | ```
 48 | 
 49 | ## Generating an n8n API Key
 50 | 
 51 | To use the n8n MCP Server, you need an API key from your n8n instance:
 52 | 
 53 | 1. Open your n8n instance in a browser
 54 | 2. Go to **Settings** > **API** > **API Keys**
 55 | 3. Click **Create** to create a new API key
 56 | 4. Set appropriate **Scope** (recommended: `workflow:read workflow:write workflow:execute`)
 57 | 5. Copy the key to your `.env` file
 58 | 
 59 | ![Creating an n8n API Key](../images/n8n-api-key.png)
 60 | 
 61 | ## Server Connection Options
 62 | 
 63 | By default, the n8n MCP Server listens on `stdin` and `stdout` for Model Context Protocol communications. This is the format expected by AI assistants using the MCP protocol.
 64 | 
 65 | ## Configuring AI Assistants
 66 | 
 67 | To use the n8n MCP Server with AI assistants, you need to register it with your AI assistant platform. The exact method depends on the platform you're using.
 68 | 
 69 | ### Using the MCP Installer
 70 | 
 71 | If you're using Claude or another assistant that supports the MCP Installer, you can register the server with:
 72 | 
 73 | ```bash
 74 | # Install the MCP Installer
 75 | npx @anaisbetts/mcp-installer
 76 | 
 77 | # Register the server (if installed globally)
 78 | install_repo_mcp_server n8n-mcp-server
 79 | 
 80 | # Or register from a local installation
 81 | install_local_mcp_server path/to/n8n-mcp-server
 82 | ```
 83 | 
 84 | ### Manual Configuration
 85 | 
 86 | For platforms without an installer, you'll need to configure the connection according to the platform's documentation. Typically, this involves:
 87 | 
 88 | 1. Specifying the path to the executable
 89 | 2. Setting environment variables for the server
 90 | 3. Configuring response formatting
 91 | 
 92 | ## Verifying Configuration
 93 | 
 94 | To verify your configuration:
 95 | 
 96 | 1. Start the server
 97 | 2. Open your AI assistant
 98 | 3. Try a simple command like "List all workflows in n8n"
 99 | 
100 | If configured correctly, the assistant should be able to retrieve and display your workflows.
101 | 
102 | ## Troubleshooting
103 | 
104 | If you encounter issues with your configuration, check:
105 | 
106 | - The `.env` file is in the correct location
107 | - The n8n API URL is accessible from where the server is running
108 | - The API key has the correct permissions
109 | - Any firewalls or network restrictions that might block connections
110 | 
111 | For more specific issues, see the [Troubleshooting](./troubleshooting.md) guide.
112 | 
```

--------------------------------------------------------------------------------
/tests/mocks/n8n-fixtures.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Mock fixtures for n8n API responses
  3 |  */
  4 | 
  5 | import { Workflow, Execution } from '../../src/types/index.js';
  6 | 
  7 | /**
  8 |  * Create a mock workflow for testing
  9 |  */
 10 | export const createMockWorkflow = (overrides: Partial<Workflow> = {}): Workflow => {
 11 |   const id = overrides.id ?? 'mock-workflow-1';
 12 |   
 13 |   return {
 14 |     id,
 15 |     name: overrides.name ?? `Mock Workflow ${id}`,
 16 |     active: overrides.active ?? false,
 17 |     createdAt: overrides.createdAt ?? new Date().toISOString(),
 18 |     updatedAt: overrides.updatedAt ?? new Date().toISOString(),
 19 |     nodes: overrides.nodes ?? [
 20 |       {
 21 |         id: 'start',
 22 |         name: 'Start',
 23 |         type: 'n8n-nodes-base.start',
 24 |         parameters: {},
 25 |         position: [100, 300],
 26 |       },
 27 |     ],
 28 |     connections: overrides.connections ?? {},
 29 |     settings: overrides.settings ?? {},
 30 |     staticData: overrides.staticData ?? null,
 31 |     pinData: overrides.pinData ?? {},
 32 |     ...overrides,
 33 |   };
 34 | };
 35 | 
 36 | /**
 37 |  * Create multiple mock workflows
 38 |  */
 39 | export const createMockWorkflows = (count: number = 3): Workflow[] => {
 40 |   return Array.from({ length: count }, (_, i) => 
 41 |     createMockWorkflow({
 42 |       id: `mock-workflow-${i + 1}`,
 43 |       name: `Mock Workflow ${i + 1}`,
 44 |       active: i % 2 === 0, // Alternate active status
 45 |     })
 46 |   );
 47 | };
 48 | 
 49 | /**
 50 |  * Create a mock execution for testing
 51 |  */
 52 | export const createMockExecution = (overrides: Partial<Execution> = {}): Execution => {
 53 |   const id = overrides.id ?? 'mock-execution-1';
 54 |   const workflowId = overrides.workflowId ?? 'mock-workflow-1';
 55 |   
 56 |   return {
 57 |     id,
 58 |     workflowId,
 59 |     finished: overrides.finished ?? true,
 60 |     mode: overrides.mode ?? 'manual',
 61 |     waitTill: overrides.waitTill ?? null,
 62 |     startedAt: overrides.startedAt ?? new Date().toISOString(),
 63 |     stoppedAt: overrides.stoppedAt ?? new Date().toISOString(),
 64 |     status: overrides.status ?? 'success',
 65 |     data: overrides.data ?? {
 66 |       resultData: {
 67 |         runData: {},
 68 |       },
 69 |     },
 70 |     workflowData: overrides.workflowData ?? createMockWorkflow({ id: workflowId }),
 71 |     ...overrides,
 72 |   };
 73 | };
 74 | 
 75 | /**
 76 |  * Create multiple mock executions
 77 |  */
 78 | export const createMockExecutions = (count: number = 3): Execution[] => {
 79 |   return Array.from({ length: count }, (_, i) => 
 80 |     createMockExecution({
 81 |       id: `mock-execution-${i + 1}`,
 82 |       workflowId: `mock-workflow-${(i % 2) + 1}`, // Alternate between two workflows
 83 |       status: i % 3 === 0 ? 'success' : i % 3 === 1 ? 'error' : 'waiting',
 84 |     })
 85 |   );
 86 | };
 87 | 
 88 | /**
 89 |  * Create mock n8n API responses
 90 |  */
 91 | export const mockApiResponses = {
 92 |   workflows: {
 93 |     list: {
 94 |       data: createMockWorkflows(),
 95 |     },
 96 |     single: (id: string = 'mock-workflow-1') => createMockWorkflow({ id }),
 97 |     create: (workflow: Partial<Workflow> = {}) => createMockWorkflow(workflow),
 98 |     update: (id: string = 'mock-workflow-1', workflow: Partial<Workflow> = {}) => 
 99 |       createMockWorkflow({ ...workflow, id }),
100 |     delete: { success: true },
101 |     activate: (id: string = 'mock-workflow-1') => createMockWorkflow({ id, active: true }),
102 |     deactivate: (id: string = 'mock-workflow-1') => createMockWorkflow({ id, active: false }),
103 |   },
104 |   
105 |   executions: {
106 |     list: {
107 |       data: createMockExecutions(),
108 |     },
109 |     single: (id: string = 'mock-execution-1') => createMockExecution({ id }),
110 |     delete: { success: true },
111 |   },
112 | };
113 | 
114 | export default {
115 |   createMockWorkflow,
116 |   createMockWorkflows,
117 |   createMockExecution,
118 |   createMockExecutions,
119 |   mockApiResponses,
120 | };
121 | 
```

--------------------------------------------------------------------------------
/src/api/n8n-client.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * n8n API Client Interface
  3 |  * 
  4 |  * This module defines interfaces and types for the n8n API client.
  5 |  */
  6 | 
  7 | import { N8nApiClient } from './client.js';
  8 | import { EnvConfig } from '../config/environment.js';
  9 | import { Workflow, Execution } from '../types/index.js';
 10 | 
 11 | /**
 12 |  * n8n API service - provides functions for interacting with n8n API
 13 |  */
 14 | export class N8nApiService {
 15 |   private client: N8nApiClient;
 16 | 
 17 |   /**
 18 |    * Create a new n8n API service
 19 |    * 
 20 |    * @param config Environment configuration
 21 |    */
 22 |   constructor(config: EnvConfig) {
 23 |     this.client = new N8nApiClient(config);
 24 |   }
 25 | 
 26 |   /**
 27 |    * Check connectivity to the n8n API
 28 |    */
 29 |   async checkConnectivity(): Promise<void> {
 30 |     return this.client.checkConnectivity();
 31 |   }
 32 | 
 33 |   /**
 34 |    * Get all workflows from n8n
 35 |    * 
 36 |    * @returns Array of workflow objects
 37 |    */
 38 |   async getWorkflows(): Promise<Workflow[]> {
 39 |     return this.client.getWorkflows();
 40 |   }
 41 | 
 42 |   /**
 43 |    * Get a specific workflow by ID
 44 |    * 
 45 |    * @param id Workflow ID
 46 |    * @returns Workflow object
 47 |    */
 48 |   async getWorkflow(id: string): Promise<Workflow> {
 49 |     return this.client.getWorkflow(id);
 50 |   }
 51 | 
 52 |   /**
 53 |    * Execute a workflow by ID
 54 |    * 
 55 |    * @param id Workflow ID
 56 |    * @param data Optional data to pass to the workflow
 57 |    * @returns Execution result
 58 |    */
 59 |   async executeWorkflow(id: string, data?: Record<string, any>): Promise<any> {
 60 |     return this.client.executeWorkflow(id, data);
 61 |   }
 62 | 
 63 |   /**
 64 |    * Create a new workflow
 65 |    * 
 66 |    * @param workflow Workflow object to create
 67 |    * @returns Created workflow
 68 |    */
 69 |   async createWorkflow(workflow: Record<string, any>): Promise<Workflow> {
 70 |     return this.client.createWorkflow(workflow);
 71 |   }
 72 | 
 73 |   /**
 74 |    * Update an existing workflow
 75 |    * 
 76 |    * @param id Workflow ID
 77 |    * @param workflow Updated workflow object
 78 |    * @returns Updated workflow
 79 |    */
 80 |   async updateWorkflow(id: string, workflow: Record<string, any>): Promise<Workflow> {
 81 |     return this.client.updateWorkflow(id, workflow);
 82 |   }
 83 | 
 84 |   /**
 85 |    * Delete a workflow
 86 |    * 
 87 |    * @param id Workflow ID
 88 |    * @returns Deleted workflow or success message
 89 |    */
 90 |   async deleteWorkflow(id: string): Promise<any> {
 91 |     return this.client.deleteWorkflow(id);
 92 |   }
 93 | 
 94 |   /**
 95 |    * Activate a workflow
 96 |    * 
 97 |    * @param id Workflow ID
 98 |    * @returns Activated workflow
 99 |    */
100 |   async activateWorkflow(id: string): Promise<Workflow> {
101 |     return this.client.activateWorkflow(id);
102 |   }
103 | 
104 |   /**
105 |    * Deactivate a workflow
106 |    * 
107 |    * @param id Workflow ID
108 |    * @returns Deactivated workflow
109 |    */
110 |   async deactivateWorkflow(id: string): Promise<Workflow> {
111 |     return this.client.deactivateWorkflow(id);
112 |   }
113 | 
114 |   /**
115 |    * Get all workflow executions
116 |    * 
117 |    * @returns Array of execution objects
118 |    */
119 |   async getExecutions(): Promise<Execution[]> {
120 |     return this.client.getExecutions();
121 |   }
122 | 
123 |   /**
124 |    * Get a specific execution by ID
125 |    * 
126 |    * @param id Execution ID
127 |    * @returns Execution object
128 |    */
129 |   async getExecution(id: string): Promise<Execution> {
130 |     return this.client.getExecution(id);
131 |   }
132 |   
133 |   /**
134 |    * Delete an execution
135 |    * 
136 |    * @param id Execution ID
137 |    * @returns Deleted execution or success message
138 |    */
139 |   async deleteExecution(id: string): Promise<any> {
140 |     return this.client.deleteExecution(id);
141 |   }
142 | }
143 | 
144 | /**
145 |  * Create a new n8n API service
146 |  * 
147 |  * @param config Environment configuration
148 |  * @returns n8n API service
149 |  */
150 | export function createApiService(config: EnvConfig): N8nApiService {
151 |   return new N8nApiService(config);
152 | }
153 | 
```

--------------------------------------------------------------------------------
/tests/unit/utils/execution-formatter.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from '@jest/globals';
  2 | import {
  3 |   formatExecutionSummary,
  4 |   formatExecutionDetails,
  5 |   getStatusIndicator,
  6 |   summarizeExecutions
  7 | } from '../../../src/utils/execution-formatter.js';
  8 | import {
  9 |   createMockExecution,
 10 |   createMockExecutions
 11 | } from '../../mocks/n8n-fixtures.js';
 12 | 
 13 | 
 14 | describe('Execution Formatter Utilities', () => {
 15 |   describe('getStatusIndicator', () => {
 16 |     it('returns the correct emoji for known statuses', () => {
 17 |       expect(getStatusIndicator('success')).toBe('✅');
 18 |       expect(getStatusIndicator('error')).toBe('❌');
 19 |       expect(getStatusIndicator('waiting')).toBe('⏳');
 20 |       expect(getStatusIndicator('canceled')).toBe('🛑');
 21 |     });
 22 | 
 23 |     it('returns a default emoji for unknown status', () => {
 24 |       expect(getStatusIndicator('unknown')).toBe('⏱️');
 25 |     });
 26 |   });
 27 | 
 28 |   describe('formatExecutionSummary', () => {
 29 |     it('formats execution data into a summary', () => {
 30 |       const execution = createMockExecution({
 31 |         id: 'exec1',
 32 |         workflowId: 'wf1',
 33 |         status: 'success',
 34 |         startedAt: '2025-01-01T00:00:00.000Z',
 35 |         stoppedAt: '2025-01-01T00:00:05.000Z'
 36 |       });
 37 | 
 38 |       const summary = formatExecutionSummary(execution);
 39 | 
 40 |       expect(summary).toMatchObject({
 41 |         id: 'exec1',
 42 |         workflowId: 'wf1',
 43 |         status: '✅ success',
 44 |         startedAt: '2025-01-01T00:00:00.000Z',
 45 |         stoppedAt: '2025-01-01T00:00:05.000Z',
 46 |         finished: true
 47 |       });
 48 |       expect(summary.duration).toBe('5s');
 49 |     });
 50 | 
 51 |     it('marks execution as in progress when stoppedAt is missing', () => {
 52 |       const execution = createMockExecution({
 53 |         stoppedAt: undefined as any,
 54 |         status: 'waiting'
 55 |       });
 56 | 
 57 |       const summary = formatExecutionSummary(execution);
 58 |       expect(summary.stoppedAt).toBe('In progress');
 59 |     });
 60 |   });
 61 | 
 62 |   describe('formatExecutionDetails', () => {
 63 |     it('includes node results when present', () => {
 64 |       const execution = createMockExecution({
 65 |         data: {
 66 |           resultData: {
 67 |             runData: {
 68 |               MyNode: [
 69 |                 {
 70 |                   status: 'success',
 71 |                   data: { main: [[{ foo: 'bar' }]] }
 72 |                 }
 73 |               ]
 74 |             }
 75 |           }
 76 |         },
 77 |         status: 'success'
 78 |       });
 79 | 
 80 |       const details = formatExecutionDetails(execution);
 81 |       expect(details.nodeResults.MyNode).toEqual({
 82 |         status: 'success',
 83 |         items: 1,
 84 |         data: [{ foo: 'bar' }]
 85 |       });
 86 |     });
 87 | 
 88 |     it('adds error information when present', () => {
 89 |       const execution = createMockExecution({
 90 |         data: {
 91 |           resultData: {
 92 |             runData: {},
 93 |             error: { message: 'boom', stack: 'trace' }
 94 |           } as any
 95 |         },
 96 |         status: 'error'
 97 |       });
 98 | 
 99 |       const details = formatExecutionDetails(execution);
100 |       expect(details.error).toEqual({ message: 'boom', stack: 'trace' });
101 |     });
102 |   });
103 | 
104 |   describe('summarizeExecutions', () => {
105 |     it('summarizes counts and percentages', () => {
106 |       const executions = [
107 |         createMockExecution({ status: 'success' }),
108 |         createMockExecution({ status: 'error' }),
109 |         createMockExecution({ status: 'waiting' }),
110 |         createMockExecution({ status: 'success' })
111 |       ];
112 | 
113 |       const summary = summarizeExecutions(executions);
114 | 
115 |       expect(summary.total).toBe(4);
116 |       const success = summary.byStatus.find((s: any) => s.status.includes('success'));
117 |       const error = summary.byStatus.find((s: any) => s.status.includes('error'));
118 |       expect(success.count).toBe(2);
119 |       expect(error.count).toBe(1);
120 |       expect(summary.successRate).toBe('50%');
121 |     });
122 |   });
123 | });
124 | 
```

--------------------------------------------------------------------------------
/docs/api/static-resources.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Static Resources
  2 | 
  3 | This page documents the static resources available in the n8n MCP Server.
  4 | 
  5 | ## Overview
  6 | 
  7 | Static resources provide access to fixed n8n data sources without requiring parameters in the URI. These resources are ideal for retrieving collections of data or summary information.
  8 | 
  9 | ## Available Resources
 10 | 
 11 | ### n8n://workflows/list
 12 | 
 13 | Provides a list of all workflows in the n8n instance.
 14 | 
 15 | **URI:** `n8n://workflows/list`
 16 | 
 17 | **Description:** Returns a comprehensive list of all workflows with their basic metadata.
 18 | 
 19 | **Example Usage:**
 20 | 
 21 | ```javascript
 22 | const resource = await accessMcpResource('n8n-mcp-server', 'n8n://workflows/list');
 23 | ```
 24 | 
 25 | **Response:**
 26 | 
 27 | ```javascript
 28 | {
 29 |   "workflows": [
 30 |     {
 31 |       "id": "1234abc",
 32 |       "name": "Email Processing Workflow",
 33 |       "active": true,
 34 |       "createdAt": "2025-03-01T12:00:00.000Z",
 35 |       "updatedAt": "2025-03-02T14:30:00.000Z"
 36 |     },
 37 |     {
 38 |       "id": "5678def",
 39 |       "name": "Data Sync Workflow",
 40 |       "active": false,
 41 |       "createdAt": "2025-03-01T12:00:00.000Z",
 42 |       "updatedAt": "2025-03-12T10:15:00.000Z"
 43 |     }
 44 |   ],
 45 |   "count": 2,
 46 |   "pagination": {
 47 |     "hasMore": false
 48 |   }
 49 | }
 50 | ```
 51 | 
 52 | ### n8n://execution-stats
 53 | 
 54 | Provides aggregated statistics about workflow executions.
 55 | 
 56 | **URI:** `n8n://execution-stats`
 57 | 
 58 | **Description:** Returns summary statistics about workflow executions, including counts by status, average execution times, and recent trends.
 59 | 
 60 | **Example Usage:**
 61 | 
 62 | ```javascript
 63 | const resource = await accessMcpResource('n8n-mcp-server', 'n8n://execution-stats');
 64 | ```
 65 | 
 66 | **Response:**
 67 | 
 68 | ```javascript
 69 | {
 70 |   "totalExecutions": 1250,
 71 |   "statusCounts": {
 72 |     "success": 1050,
 73 |     "error": 180,
 74 |     "cancelled": 20
 75 |   },
 76 |   "averageExecutionTime": 3.5, // seconds
 77 |   "recentActivity": {
 78 |     "last24Hours": 125,
 79 |     "last7Days": 450
 80 |   },
 81 |   "topWorkflows": [
 82 |     {
 83 |       "id": "1234abc",
 84 |       "name": "Email Processing Workflow",
 85 |       "executionCount": 256
 86 |     },
 87 |     {
 88 |       "id": "5678def",
 89 |       "name": "Data Sync Workflow",
 90 |       "executionCount": 198
 91 |     }
 92 |   ]
 93 | }
 94 | ```
 95 | 
 96 | ### n8n://health
 97 | 
 98 | Provides health information about the n8n instance.
 99 | 
100 | **URI:** `n8n://health`
101 | 
102 | **Description:** Returns health status information about the n8n instance including connection status, version, and basic metrics.
103 | 
104 | **Example Usage:**
105 | 
106 | ```javascript
107 | const resource = await accessMcpResource('n8n-mcp-server', 'n8n://health');
108 | ```
109 | 
110 | **Response:**
111 | 
112 | ```javascript
113 | {
114 |   "status": "healthy",
115 |   "n8nVersion": "1.5.0",
116 |   "uptime": 259200, // seconds (3 days)
117 |   "databaseStatus": "connected",
118 |   "apiStatus": "operational",
119 |   "memoryUsage": {
120 |     "rss": "156MB",
121 |     "heapTotal": "85MB",
122 |     "heapUsed": "72MB"
123 |   }
124 | }
125 | ```
126 | 
127 | ## Content Types
128 | 
129 | All static resources return JSON content with the MIME type `application/json`.
130 | 
131 | ## Authentication
132 | 
133 | Access to static resources requires the same authentication as tools, using the configured n8n API key. If authentication fails, the resource will return an error.
134 | 
135 | ## Error Handling
136 | 
137 | Static resources can return the following errors:
138 | 
139 | | HTTP Status | Description |
140 | |-------------|-------------|
141 | | 401 | Unauthorized - Invalid or missing API key |
142 | | 403 | Forbidden - API key does not have permission to access this resource |
143 | | 500 | Internal Server Error - An unexpected error occurred on the n8n server |
144 | 
145 | ## Pagination
146 | 
147 | Some resources that return large collections (like `n8n://workflows/list`) support pagination. The response includes a `pagination` object with information about whether more results are available.
148 | 
149 | ## Best Practices
150 | 
151 | - Use static resources for getting an overview of what's available in the n8n instance
152 | - Prefer static resources over tools when you only need to read data
153 | - Check the health resource before performing operations to ensure the n8n instance is operational
154 | - Use execution statistics to monitor the performance and reliability of your workflows
155 | 
```

--------------------------------------------------------------------------------
/src/utils/resource-formatter.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Resource Formatter Utilities
  3 |  * 
  4 |  * This module provides utility functions for formatting resource data
  5 |  * in a consistent, user-friendly manner for MCP resources.
  6 |  */
  7 | 
  8 | import { Workflow, Execution } from '../types/index.js';
  9 | 
 10 | /**
 11 |  * Format workflow summary for static resource listing
 12 |  * 
 13 |  * @param workflow Workflow object
 14 |  * @returns Formatted workflow summary
 15 |  */
 16 | export function formatWorkflowSummary(workflow: Workflow): Record<string, any> {
 17 |   return {
 18 |     id: workflow.id,
 19 |     name: workflow.name,
 20 |     active: workflow.active,
 21 |     status: workflow.active ? '🟢 Active' : '⚪ Inactive',
 22 |     updatedAt: workflow.updatedAt,
 23 |     createdAt: workflow.createdAt,
 24 |   };
 25 | }
 26 | 
 27 | /**
 28 |  * Format detailed workflow information for dynamic resources
 29 |  * 
 30 |  * @param workflow Workflow object
 31 |  * @returns Formatted workflow details
 32 |  */
 33 | export function formatWorkflowDetails(workflow: Workflow): Record<string, any> {
 34 |   const summary = formatWorkflowSummary(workflow);
 35 |   
 36 |   // Add additional details
 37 |   return {
 38 |     ...summary,
 39 |     nodes: workflow.nodes.map(node => ({
 40 |       id: node.id,
 41 |       name: node.name,
 42 |       type: node.type,
 43 |       position: node.position,
 44 |       parameters: node.parameters,
 45 |     })),
 46 |     connections: workflow.connections,
 47 |     staticData: workflow.staticData,
 48 |     settings: workflow.settings,
 49 |     tags: workflow.tags,
 50 |     // Exclude potentially sensitive or unnecessary information
 51 |     // like pinData or other internal fields
 52 |   };
 53 | }
 54 | 
 55 | /**
 56 |  * Format execution statistics summary
 57 |  * 
 58 |  * @param executions Array of execution objects
 59 |  * @returns Formatted execution statistics
 60 |  */
 61 | export function formatExecutionStats(executions: Execution[]): Record<string, any> {
 62 |   // Group executions by status
 63 |   const statusCounts: Record<string, number> = {};
 64 |   executions.forEach(execution => {
 65 |     const status = execution.status || 'unknown';
 66 |     statusCounts[status] = (statusCounts[status] || 0) + 1;
 67 |   });
 68 |   
 69 |   // Calculate success rate
 70 |   const totalCount = executions.length;
 71 |   const successCount = statusCounts.success || 0;
 72 |   const successRate = totalCount > 0 ? Math.round((successCount / totalCount) * 100) : 0;
 73 |   
 74 |   // Calculate average execution time
 75 |   let totalDuration = 0;
 76 |   let completedCount = 0;
 77 |   
 78 |   executions.forEach(execution => {
 79 |     if (execution.startedAt && execution.stoppedAt) {
 80 |       const startedAt = new Date(execution.startedAt);
 81 |       const stoppedAt = new Date(execution.stoppedAt);
 82 |       const durationMs = stoppedAt.getTime() - startedAt.getTime();
 83 |       
 84 |       totalDuration += durationMs;
 85 |       completedCount++;
 86 |     }
 87 |   });
 88 |   
 89 |   const avgDurationMs = completedCount > 0 ? Math.round(totalDuration / completedCount) : 0;
 90 |   const avgDurationSec = Math.round(avgDurationMs / 1000);
 91 |   
 92 |   // Group executions by workflow
 93 |   const workflowExecutions: Record<string, number> = {};
 94 |   executions.forEach(execution => {
 95 |     const workflowId = execution.workflowId;
 96 |     workflowExecutions[workflowId] = (workflowExecutions[workflowId] || 0) + 1;
 97 |   });
 98 |   
 99 |   // Get top workflows by execution count
100 |   const topWorkflows = Object.entries(workflowExecutions)
101 |     .sort((a, b) => b[1] - a[1])
102 |     .slice(0, 5)
103 |     .map(([workflowId, count]) => ({
104 |       workflowId,
105 |       executionCount: count,
106 |       percentage: totalCount > 0 ? Math.round((count / totalCount) * 100) : 0
107 |     }));
108 |   
109 |   return {
110 |     total: totalCount,
111 |     byStatus: Object.entries(statusCounts).map(([status, count]) => ({
112 |       status,
113 |       count,
114 |       percentage: totalCount > 0 ? Math.round((count / totalCount) * 100) : 0
115 |     })),
116 |     successRate: `${successRate}%`,
117 |     averageExecutionTime: completedCount > 0 ? `${avgDurationSec}s` : 'N/A',
118 |     recentTrend: {
119 |       // Recent executions trend - last 24 hours vs previous 24 hours
120 |       // This is a placeholder - would need timestamp filtering logic
121 |       changePercent: '0%',
122 |       description: 'Stable execution rate'
123 |     },
124 |     topWorkflows: topWorkflows,
125 |     timeUpdated: new Date().toISOString()
126 |   };
127 | }
128 | 
129 | /**
130 |  * Format resource URI for n8n resources
131 |  * 
132 |  * @param resourceType Type of resource (workflow or execution)
133 |  * @param id Optional resource ID for specific resources
134 |  * @returns Formatted resource URI
135 |  */
136 | export function formatResourceUri(
137 |   resourceType: 'workflow' | 'execution' | 'workflows' | 'execution-stats',
138 |   id?: string,
139 | ): string {
140 |   if (id) {
141 |     const base = ['workflow', 'execution'].includes(resourceType)
142 |       ? `${resourceType}s`
143 |       : resourceType;
144 |     return `n8n://${base}/${id}`;
145 |   }
146 |   return `n8n://${resourceType}`;
147 | }
148 | 
```

--------------------------------------------------------------------------------
/src/utils/execution-formatter.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Execution Formatter Utilities
  3 |  * 
  4 |  * This module provides utility functions for formatting execution data
  5 |  * in a consistent, user-friendly manner.
  6 |  */
  7 | 
  8 | import { Execution } from '../types/index.js';
  9 | 
 10 | /**
 11 |  * Format basic execution information for display
 12 |  * 
 13 |  * @param execution Execution object
 14 |  * @returns Formatted execution summary
 15 |  */
 16 | export function formatExecutionSummary(execution: Execution): Record<string, any> {
 17 |   // Calculate duration
 18 |   const startedAt = new Date(execution.startedAt);
 19 |   const stoppedAt = execution.stoppedAt ? new Date(execution.stoppedAt) : new Date();
 20 |   const durationMs = stoppedAt.getTime() - startedAt.getTime();
 21 |   const durationSeconds = Math.round(durationMs / 1000);
 22 |   
 23 |   // Create status indicator emoji
 24 |   const statusIndicator = getStatusIndicator(execution.status);
 25 |   
 26 |   return {
 27 |     id: execution.id,
 28 |     workflowId: execution.workflowId,
 29 |     status: `${statusIndicator} ${execution.status}`,
 30 |     startedAt: execution.startedAt,
 31 |     stoppedAt: execution.stoppedAt || 'In progress',
 32 |     duration: `${durationSeconds}s`,
 33 |     finished: execution.finished
 34 |   };
 35 | }
 36 | 
 37 | /**
 38 |  * Format detailed execution information including node results
 39 |  * 
 40 |  * @param execution Execution object
 41 |  * @returns Formatted execution details
 42 |  */
 43 | export function formatExecutionDetails(execution: Execution): Record<string, any> {
 44 |   const summary = formatExecutionSummary(execution);
 45 |   
 46 |   // Extract node results
 47 |   const nodeResults: Record<string, any> = {};
 48 |   if (execution.data?.resultData?.runData) {
 49 |     for (const [nodeName, nodeData] of Object.entries(execution.data.resultData.runData)) {
 50 |       try {
 51 |         // Get the last output
 52 |         const lastOutput = Array.isArray(nodeData) && nodeData.length > 0
 53 |           ? nodeData[nodeData.length - 1]
 54 |           : null;
 55 |           
 56 |         if (lastOutput && lastOutput.data && Array.isArray(lastOutput.data.main)) {
 57 |           // Extract the output data
 58 |           const outputData = lastOutput.data.main.length > 0 
 59 |             ? lastOutput.data.main[0]
 60 |             : [];
 61 |             
 62 |           nodeResults[nodeName] = {
 63 |             status: lastOutput.status,
 64 |             items: outputData.length,
 65 |             data: outputData.slice(0, 3), // Limit to first 3 items to avoid overwhelming response
 66 |           };
 67 |         }
 68 |       } catch (error) {
 69 |         nodeResults[nodeName] = { error: 'Failed to parse node output' };
 70 |       }
 71 |     }
 72 |   }
 73 |   
 74 |   // Add node results and error information to the summary
 75 |   return {
 76 |     ...summary,
 77 |     mode: execution.mode,
 78 |     nodeResults: nodeResults,
 79 |     // Include error information if present
 80 |     error: execution.data?.resultData && 'error' in execution.data.resultData
 81 |       ? {
 82 |           message: (execution.data.resultData as any).error?.message,
 83 |           stack: (execution.data.resultData as any).error?.stack,
 84 |         }
 85 |       : undefined,
 86 |   };
 87 | }
 88 | 
 89 | /**
 90 |  * Get appropriate status indicator emoji based on execution status
 91 |  * 
 92 |  * @param status Execution status string
 93 |  * @returns Status indicator emoji
 94 |  */
 95 | export function getStatusIndicator(status: string): string {
 96 |   switch (status) {
 97 |     case 'success':
 98 |       return '✅'; // Success
 99 |     case 'error':
100 |       return '❌'; // Error
101 |     case 'waiting':
102 |       return '⏳'; // Waiting
103 |     case 'canceled':
104 |       return '🛑'; // Canceled
105 |     default:
106 |       return '⏱️'; // In progress or unknown
107 |   }
108 | }
109 | 
110 | /**
111 |  * Summarize execution results for more compact display
112 |  * 
113 |  * @param executions Array of execution objects
114 |  * @param limit Maximum number of executions to include
115 |  * @returns Summary of execution results
116 |  */
117 | export function summarizeExecutions(executions: Execution[], limit: number = 10): Record<string, any> {
118 |   const limitedExecutions = executions.slice(0, limit);
119 |   
120 |   // Group executions by status
121 |   const byStatus: Record<string, number> = {};
122 |   limitedExecutions.forEach(execution => {
123 |     const status = execution.status || 'unknown';
124 |     byStatus[status] = (byStatus[status] || 0) + 1;
125 |   });
126 |   
127 |   // Calculate success rate
128 |   const totalCount = limitedExecutions.length;
129 |   const successCount = byStatus.success || 0;
130 |   const successRate = totalCount > 0 ? Math.round((successCount / totalCount) * 100) : 0;
131 |   
132 |   return {
133 |     total: totalCount,
134 |     byStatus: Object.entries(byStatus).map(([status, count]) => ({
135 |       status: `${getStatusIndicator(status)} ${status}`,
136 |       count,
137 |       percentage: totalCount > 0 ? Math.round((count / totalCount) * 100) : 0
138 |     })),
139 |     successRate: `${successRate}%`,
140 |     displayed: limitedExecutions.length,
141 |     totalAvailable: executions.length
142 |   };
143 | }
144 | 
```

--------------------------------------------------------------------------------
/src/tools/workflow/update.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Update Workflow Tool
  3 |  * 
  4 |  * This tool updates an existing workflow in n8n.
  5 |  */
  6 | 
  7 | import { BaseWorkflowToolHandler } from './base-handler.js';
  8 | import { ToolCallResult, ToolDefinition } from '../../types/index.js';
  9 | import { N8nApiError } from '../../errors/index.js';
 10 | 
 11 | /**
 12 |  * Handler for the update_workflow tool
 13 |  */
 14 | export class UpdateWorkflowHandler extends BaseWorkflowToolHandler {
 15 |   /**
 16 |    * Execute the tool
 17 |    * 
 18 |    * @param args Tool arguments containing workflow updates
 19 |    * @returns Updated workflow information
 20 |    */
 21 |   async execute(args: Record<string, any>): Promise<ToolCallResult> {
 22 |     return this.handleExecution(async (args) => {
 23 |       const { workflowId, name, nodes, connections, active, tags } = args;
 24 |       
 25 |       if (!workflowId) {
 26 |         throw new N8nApiError('Missing required parameter: workflowId');
 27 |       }
 28 |       
 29 |       // Validate nodes if provided
 30 |       if (nodes && !Array.isArray(nodes)) {
 31 |         throw new N8nApiError('Parameter "nodes" must be an array');
 32 |       }
 33 |       
 34 |       // Validate connections if provided
 35 |       if (connections && typeof connections !== 'object') {
 36 |         throw new N8nApiError('Parameter "connections" must be an object');
 37 |       }
 38 |       
 39 |       // Get the current workflow to update
 40 |       const currentWorkflow = await this.apiService.getWorkflow(workflowId);
 41 |       
 42 |       // Prepare update object with only allowed properties (per n8n API schema)
 43 |       const workflowData: Record<string, any> = {
 44 |         name: name !== undefined ? name : currentWorkflow.name,
 45 |         nodes: nodes !== undefined ? nodes : currentWorkflow.nodes,
 46 |         connections: connections !== undefined ? connections : currentWorkflow.connections,
 47 |         settings: currentWorkflow.settings
 48 |       };
 49 |       
 50 |       // Add optional staticData if it exists
 51 |       if (currentWorkflow.staticData !== undefined) {
 52 |         workflowData.staticData = currentWorkflow.staticData;
 53 |       }
 54 |       
 55 |       // Note: active and tags are read-only properties and cannot be updated via this endpoint
 56 |       
 57 |       // Update the workflow
 58 |       const updatedWorkflow = await this.apiService.updateWorkflow(workflowId, workflowData);
 59 |       
 60 |       // Build a summary of changes
 61 |       const changesArray = [];
 62 |       if (name !== undefined && name !== currentWorkflow.name) changesArray.push(`name: "${currentWorkflow.name}" → "${name}"`);
 63 |       if (nodes !== undefined) changesArray.push('nodes updated');
 64 |       if (connections !== undefined) changesArray.push('connections updated');
 65 |       
 66 |       // Add warnings for read-only properties
 67 |       const warnings = [];
 68 |       if (active !== undefined) warnings.push('active (read-only, use activate/deactivate workflow tools)');
 69 |       if (tags !== undefined) warnings.push('tags (read-only property)');
 70 |       
 71 |       const changesSummary = changesArray.length > 0
 72 |         ? `Changes: ${changesArray.join(', ')}`
 73 |         : 'No changes were made';
 74 |       
 75 |       const warningsSummary = warnings.length > 0
 76 |         ? ` Note: Ignored ${warnings.join(', ')}.`
 77 |         : '';
 78 |       
 79 |       return this.formatSuccess(
 80 |         {
 81 |           id: updatedWorkflow.id,
 82 |           name: updatedWorkflow.name,
 83 |           active: updatedWorkflow.active
 84 |         },
 85 |         `Workflow updated successfully. ${changesSummary}${warningsSummary}`
 86 |       );
 87 |     }, args);
 88 |   }
 89 | }
 90 | 
 91 | /**
 92 |  * Get tool definition for the update_workflow tool
 93 |  * 
 94 |  * @returns Tool definition
 95 |  */
 96 | export function getUpdateWorkflowToolDefinition(): ToolDefinition {
 97 |   return {
 98 |     name: 'update_workflow',
 99 |     description: 'Update an existing workflow in n8n',
100 |     inputSchema: {
101 |       type: 'object',
102 |       properties: {
103 |         workflowId: {
104 |           type: 'string',
105 |           description: 'ID of the workflow to update',
106 |         },
107 |         name: {
108 |           type: 'string',
109 |           description: 'New name for the workflow',
110 |         },
111 |         nodes: {
112 |           type: 'array',
113 |           description: 'Updated array of node objects that define the workflow',
114 |           items: {
115 |             type: 'object',
116 |           },
117 |         },
118 |         connections: {
119 |           type: 'object',
120 |           description: 'Updated connection mappings between nodes',
121 |         },
122 |         active: {
123 |           type: 'boolean',
124 |           description: 'Whether the workflow should be active (read-only: use activate/deactivate workflow tools instead)',
125 |         },
126 |         tags: {
127 |           type: 'array',
128 |           description: 'Tags to associate with the workflow (read-only: cannot be updated via this endpoint)',
129 |           items: {
130 |             type: 'string',
131 |           },
132 |         },
133 |       },
134 |       required: ['workflowId'],
135 |     },
136 |   };
137 | }
138 | 
```

--------------------------------------------------------------------------------
/src/resources/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Resources Module
  3 |  * 
  4 |  * This module provides MCP resource handlers for n8n workflows and executions.
  5 |  */
  6 | 
  7 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  8 | import {
  9 |   ListResourcesRequestSchema,
 10 |   ListResourceTemplatesRequestSchema,
 11 |   ReadResourceRequestSchema,
 12 | } from '@modelcontextprotocol/sdk/types.js';
 13 | import { EnvConfig } from '../config/environment.js';
 14 | import { createApiService } from '../api/n8n-client.js';
 15 | import { McpError, ErrorCode } from '../errors/index.js';
 16 | 
 17 | // Import static resource handlers
 18 | import { 
 19 |   getWorkflowsResource,
 20 |   getWorkflowsResourceMetadata,
 21 |   getWorkflowsResourceUri,
 22 | } from './static/workflows.js';
 23 | import {
 24 |   getExecutionStatsResource,
 25 |   getExecutionStatsResourceMetadata,
 26 |   getExecutionStatsResourceUri,
 27 | } from './static/execution-stats.js';
 28 | 
 29 | // Import dynamic resource handlers
 30 | import {
 31 |   getWorkflowResource,
 32 |   getWorkflowResourceTemplateMetadata,
 33 |   extractWorkflowIdFromUri,
 34 | } from './dynamic/workflow.js';
 35 | import {
 36 |   getExecutionResource,
 37 |   getExecutionResourceTemplateMetadata,
 38 |   extractExecutionIdFromUri,
 39 | } from './dynamic/execution.js';
 40 | 
 41 | /**
 42 |  * Set up resource handlers for the MCP server
 43 |  * 
 44 |  * @param server MCP server instance
 45 |  * @param envConfig Environment configuration
 46 |  */
 47 | export function setupResourceHandlers(server: Server, envConfig: EnvConfig): void {
 48 |   // Set up static resources
 49 |   setupStaticResources(server, envConfig);
 50 | 
 51 |   // Set up dynamic resources
 52 |   setupDynamicResources(server, envConfig);
 53 | }
 54 | 
 55 | /**
 56 |  * Set up static resource handlers
 57 |  * 
 58 |  * @param server MCP server instance
 59 |  * @param envConfig Environment configuration
 60 |  */
 61 | function setupStaticResources(server: Server, _envConfig: EnvConfig): void {
 62 |   server.setRequestHandler(ListResourcesRequestSchema, async () => {
 63 |     // Return all available static resources
 64 |     return {
 65 |       resources: [
 66 |         getWorkflowsResourceMetadata(),
 67 |         getExecutionStatsResourceMetadata(),
 68 |       ],
 69 |     };
 70 |   });
 71 | }
 72 | 
 73 | /**
 74 |  * Set up dynamic resource handlers
 75 |  * 
 76 |  * @param server MCP server instance
 77 |  * @param envConfig Environment configuration
 78 |  */
 79 | function setupDynamicResources(server: Server, envConfig: EnvConfig): void {
 80 |   const apiService = createApiService(envConfig);
 81 |   
 82 |   server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
 83 |     // Return all available dynamic resource templates
 84 |     return {
 85 |       resourceTemplates: [
 86 |         getWorkflowResourceTemplateMetadata(),
 87 |         getExecutionResourceTemplateMetadata(),
 88 |       ],
 89 |     };
 90 |   });
 91 | 
 92 |   server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
 93 |     const uri = request.params.uri;
 94 |     console.log(`Resource requested: ${uri}`);
 95 |     
 96 |     try {
 97 |       // Handle static resources
 98 |       if (uri === getWorkflowsResourceUri()) {
 99 |         const content = await getWorkflowsResource(apiService);
100 |         return {
101 |           contents: [
102 |             {
103 |               uri,
104 |               mimeType: 'application/json',
105 |               text: content,
106 |             },
107 |           ],
108 |         };
109 |       }
110 |       
111 |       if (uri === getExecutionStatsResourceUri()) {
112 |         const content = await getExecutionStatsResource(apiService);
113 |         return {
114 |           contents: [
115 |             {
116 |               uri,
117 |               mimeType: 'application/json',
118 |               text: content,
119 |             },
120 |           ],
121 |         };
122 |       }
123 |       
124 |       // Handle dynamic resources
125 |       const workflowId = extractWorkflowIdFromUri(uri);
126 |       if (workflowId) {
127 |         const content = await getWorkflowResource(apiService, workflowId);
128 |         return {
129 |           contents: [
130 |             {
131 |               uri,
132 |               mimeType: 'application/json',
133 |               text: content,
134 |             },
135 |           ],
136 |         };
137 |       }
138 |       
139 |       const executionId = extractExecutionIdFromUri(uri);
140 |       if (executionId) {
141 |         const content = await getExecutionResource(apiService, executionId);
142 |         return {
143 |           contents: [
144 |             {
145 |               uri,
146 |               mimeType: 'application/json',
147 |               text: content,
148 |             },
149 |           ],
150 |         };
151 |       }
152 |       
153 |       // If we get here, the URI isn't recognized
154 |       throw new McpError(
155 |         ErrorCode.NotFoundError,
156 |         `Resource not found: ${uri}`
157 |       );
158 |     } catch (error) {
159 |       console.error(`Error retrieving resource (${uri}):`, error);
160 |       
161 |       // Pass through McpErrors
162 |       if (error instanceof McpError) {
163 |         throw error;
164 |       }
165 |       
166 |       // Convert other errors to McpError
167 |       throw new McpError(
168 |         ErrorCode.InternalError,
169 |         `Error retrieving resource: ${error instanceof Error ? error.message : 'Unknown error'}`
170 |       );
171 |     }
172 |   });
173 | }
174 | 
```
Page 1/3FirstPrevNextLast