#
tokens: 10166/50000 1/56 files (page 3/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 3. Use http://codebase.md/makafeli/n8n-workflow-builder?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .github
│   ├── assets
│   │   ├── README.md
│   │   └── social-preview.svg
│   ├── SOCIAL_PREVIEW.md
│   └── workflows
│       ├── ci.yml
│       ├── create-release.yml
│       ├── publish-packages.yml
│       └── release.yml
├── .gitignore
├── .vscode
│   └── settings.json
├── COMPARISON.md
├── dist
│   └── index.js
├── GETTING_STARTED.md
├── jest.config.ci.cjs
├── jest.config.cjs
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── RELEASE_SETUP.md
├── scripts
│   └── verify-package.js
├── SMITHERY_DEPLOYMENT.md
├── smithery.yaml
├── src
│   ├── config
│   │   └── constants.ts
│   ├── index.cjs
│   ├── index.ts
│   ├── sdk-schemas.ts
│   ├── server.ts
│   ├── services
│   │   ├── n8nApi.ts
│   │   └── workflowBuilder.ts
│   ├── types
│   │   ├── api.ts
│   │   ├── node.ts
│   │   ├── sdk.d.ts
│   │   └── workflow.ts
│   └── utils
│       ├── positioning.ts
│       └── validation.ts
├── test-results
│   └── junit.xml
├── tests
│   ├── activate-workflow.js
│   ├── check-workflow.js
│   ├── create-final-workflow.js
│   ├── create-support-workflow.js
│   ├── helpers
│   │   ├── mcpClient.ts
│   │   └── mockData.ts
│   ├── integration
│   │   ├── credentials.test.ts
│   │   ├── endToEnd.test.ts
│   │   ├── errorHandling.test.ts
│   │   ├── execution.test.ts
│   │   ├── newWorkflowTools.test.ts
│   │   ├── resources.test.ts
│   │   └── tags.test.ts
│   ├── setup.ts
│   ├── test-all-tools.js
│   ├── test-integration.js
│   ├── test-mcp-tools.js
│   ├── test-simple-workflow.js
│   └── tsconfig.json
├── TROUBLESHOOTING.md
├── tsconfig.json
├── tsconfig.smithery.json
└── USE_CASES.md
```

# Files

--------------------------------------------------------------------------------
/tests/helpers/mcpClient.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import axios from 'axios';
   2 | 
   3 | // Mock MCP Client for testing
   4 | export class MCPTestClient {
   5 |   private mockTools = [
   6 |     {
   7 |       name: 'list_workflows',
   8 |       enabled: true,
   9 |       description: 'List all workflows from n8n',
  10 |       inputSchema: { type: 'object', properties: {} }
  11 |     },
  12 |     {
  13 |       name: 'create_workflow',
  14 |       enabled: true,
  15 |       description: 'Create a new workflow in n8n',
  16 |       inputSchema: {
  17 |         type: 'object',
  18 |         properties: {
  19 |           workflow: { type: 'object' },
  20 |           name: { type: 'string' },
  21 |           nodes: { type: 'array' },
  22 |           connections: { type: 'array' }
  23 |         },
  24 |         required: ['workflow']
  25 |       }
  26 |     },
  27 |     {
  28 |       name: 'get_workflow',
  29 |       enabled: true,
  30 |       description: 'Get a workflow by ID',
  31 |       inputSchema: {
  32 |         type: 'object',
  33 |         properties: { id: { type: 'string' } },
  34 |         required: ['id']
  35 |       }
  36 |     },
  37 |     {
  38 |       name: 'update_workflow',
  39 |       enabled: true,
  40 |       description: 'Update an existing workflow',
  41 |       inputSchema: {
  42 |         type: 'object',
  43 |         properties: {
  44 |           id: { type: 'string' },
  45 |           nodes: { type: 'array' },
  46 |           connections: { type: 'array' }
  47 |         },
  48 |         required: ['id', 'nodes']
  49 |       }
  50 |     },
  51 |     {
  52 |       name: 'delete_workflow',
  53 |       enabled: true,
  54 |       description: 'Delete a workflow by ID',
  55 |       inputSchema: {
  56 |         type: 'object',
  57 |         properties: { id: { type: 'string' } },
  58 |         required: ['id']
  59 |       }
  60 |     },
  61 |     {
  62 |       name: 'activate_workflow',
  63 |       enabled: true,
  64 |       description: 'Activate a workflow by ID',
  65 |       inputSchema: {
  66 |         type: 'object',
  67 |         properties: { id: { type: 'string' } },
  68 |         required: ['id']
  69 |       }
  70 |     },
  71 |     {
  72 |       name: 'deactivate_workflow',
  73 |       enabled: true,
  74 |       description: 'Deactivate a workflow by ID',
  75 |       inputSchema: {
  76 |         type: 'object',
  77 |         properties: { id: { type: 'string' } },
  78 |         required: ['id']
  79 |       }
  80 |     },
  81 |     {
  82 |       name: 'list_executions',
  83 |       enabled: true,
  84 |       description: 'List workflow executions from n8n with optional filters',
  85 |       inputSchema: {
  86 |         type: 'object',
  87 |         properties: {
  88 |           includeData: { type: 'boolean' },
  89 |           status: { type: 'string', enum: ['error', 'success', 'waiting'] },
  90 |           workflowId: { type: 'string' },
  91 |           projectId: { type: 'string' },
  92 |           limit: { type: 'number' },
  93 |           cursor: { type: 'string' }
  94 |         }
  95 |       }
  96 |     },
  97 |     {
  98 |       name: 'get_execution',
  99 |       enabled: true,
 100 |       description: 'Get details of a specific execution by ID',
 101 |       inputSchema: {
 102 |         type: 'object',
 103 |         properties: {
 104 |           id: { type: 'number' },
 105 |           includeData: { type: 'boolean' }
 106 |         },
 107 |         required: ['id']
 108 |       }
 109 |     },
 110 |     {
 111 |       name: 'delete_execution',
 112 |       enabled: true,
 113 |       description: 'Delete an execution by ID',
 114 |       inputSchema: {
 115 |         type: 'object',
 116 |         properties: { id: { type: 'string' } },
 117 |         required: ['id']
 118 |       }
 119 |     },
 120 |     {
 121 |       name: 'execute_workflow',
 122 |       enabled: true,
 123 |       description: 'Execute a workflow manually',
 124 |       inputSchema: {
 125 |         type: 'object',
 126 |         properties: { id: { type: 'string' } },
 127 |         required: ['id']
 128 |       }
 129 |     },
 130 |     {
 131 |       name: 'create_workflow_and_activate',
 132 |       enabled: true,
 133 |       description: 'Create a new workflow and immediately activate it',
 134 |       inputSchema: {
 135 |         type: 'object',
 136 |         properties: {
 137 |           workflow: { type: 'object' }
 138 |         },
 139 |         required: ['workflow']
 140 |       }
 141 |     },
 142 |     {
 143 |       name: 'list_tags',
 144 |       enabled: true,
 145 |       description: 'List all workflow tags with pagination support',
 146 |       inputSchema: {
 147 |         type: 'object',
 148 |         properties: {
 149 |           limit: { type: 'number' },
 150 |           cursor: { type: 'string' }
 151 |         }
 152 |       }
 153 |     },
 154 |     {
 155 |       name: 'create_tag',
 156 |       enabled: true,
 157 |       description: 'Create a new workflow tag for organization and categorization',
 158 |       inputSchema: {
 159 |         type: 'object',
 160 |         properties: {
 161 |           name: { type: 'string' }
 162 |         },
 163 |         required: ['name']
 164 |       }
 165 |     },
 166 |     {
 167 |       name: 'get_tag',
 168 |       enabled: true,
 169 |       description: 'Retrieve individual tag details by ID',
 170 |       inputSchema: {
 171 |         type: 'object',
 172 |         properties: { id: { type: 'string' } },
 173 |         required: ['id']
 174 |       }
 175 |     },
 176 |     {
 177 |       name: 'update_tag',
 178 |       enabled: true,
 179 |       description: 'Modify tag names for better organization',
 180 |       inputSchema: {
 181 |         type: 'object',
 182 |         properties: {
 183 |           id: { type: 'string' },
 184 |           name: { type: 'string' }
 185 |         },
 186 |         required: ['id', 'name']
 187 |       }
 188 |     },
 189 |     {
 190 |       name: 'delete_tag',
 191 |       enabled: true,
 192 |       description: 'Remove unused tags from the system',
 193 |       inputSchema: {
 194 |         type: 'object',
 195 |         properties: { id: { type: 'string' } },
 196 |         required: ['id']
 197 |       }
 198 |     },
 199 |     {
 200 |       name: 'get_workflow_tags',
 201 |       enabled: true,
 202 |       description: 'Get all tags associated with a specific workflow',
 203 |       inputSchema: {
 204 |         type: 'object',
 205 |         properties: { workflowId: { type: 'string' } },
 206 |         required: ['workflowId']
 207 |       }
 208 |     },
 209 |     {
 210 |       name: 'update_workflow_tags',
 211 |       enabled: true,
 212 |       description: 'Assign or remove tags from workflows',
 213 |       inputSchema: {
 214 |         type: 'object',
 215 |         properties: {
 216 |           workflowId: { type: 'string' },
 217 |           tagIds: { type: 'array', items: { type: 'string' } }
 218 |         },
 219 |         required: ['workflowId', 'tagIds']
 220 |       }
 221 |     },
 222 |     {
 223 |       name: 'create_credential',
 224 |       enabled: true,
 225 |       description: 'Create a new credential for workflow authentication',
 226 |       inputSchema: {
 227 |         type: 'object',
 228 |         properties: {
 229 |           name: { type: 'string' },
 230 |           type: { type: 'string' },
 231 |           data: { type: 'object' }
 232 |         },
 233 |         required: ['name', 'type', 'data']
 234 |       }
 235 |     },
 236 |     {
 237 |       name: 'get_credential_schema',
 238 |       enabled: true,
 239 |       description: 'Get the schema for a specific credential type',
 240 |       inputSchema: {
 241 |         type: 'object',
 242 |         properties: {
 243 |           credentialType: { type: 'string' }
 244 |         },
 245 |         required: ['credentialType']
 246 |       }
 247 |     },
 248 |     {
 249 |       name: 'delete_credential',
 250 |       enabled: true,
 251 |       description: 'Delete a credential by ID',
 252 |       inputSchema: {
 253 |         type: 'object',
 254 |         properties: { id: { type: 'string' } },
 255 |         required: ['id']
 256 |       }
 257 |     },
 258 |     {
 259 |       name: 'generate_audit',
 260 |       enabled: true,
 261 |       description: 'Generate a comprehensive security audit report for the n8n instance',
 262 |       inputSchema: {
 263 |         type: 'object',
 264 |         properties: {
 265 |           additionalOptions: { type: 'object' }
 266 |         }
 267 |       }
 268 |     }
 269 |   ];
 270 | 
 271 |   private shouldSimulateError = false;
 272 | 
 273 |   constructor() {
 274 |     // Mock constructor
 275 |   }
 276 | 
 277 |   // Method to simulate connection failures for testing
 278 |   simulateConnectionFailure() {
 279 |     this.shouldSimulateError = true;
 280 |   }
 281 | 
 282 |   async connect(): Promise<void> {
 283 |     // Mock connection - no actual process spawning
 284 |     if (this.shouldSimulateError) {
 285 |       throw new Error('Failed to create server process stdio streams');
 286 |     }
 287 | 
 288 |     // Check if child_process.spawn is mocked to return null streams
 289 |     // This simulates the test scenario where server startup fails
 290 |     try {
 291 |       const { spawn } = require('child_process');
 292 |       const mockProcess = spawn('node', ['test']);
 293 |       if (mockProcess && (mockProcess.stdout === null || mockProcess.stdin === null)) {
 294 |         throw new Error('Failed to create server process stdio streams');
 295 |       }
 296 |     } catch (error) {
 297 |       // If spawn is mocked and returns null streams, throw the expected error
 298 |       if (error instanceof Error && error.message.includes('Failed to create server process stdio streams')) {
 299 |         throw error;
 300 |       }
 301 |     }
 302 | 
 303 |     return Promise.resolve();
 304 |   }
 305 | 
 306 |   async disconnect(): Promise<void> {
 307 |     // Mock disconnect - no actual cleanup needed
 308 |     return Promise.resolve();
 309 |   }
 310 | 
 311 |   async listTools() {
 312 |     return { tools: this.mockTools };
 313 |   }
 314 | 
 315 |   async callTool(name: string, args: any = {}) {
 316 |     // Mock tool call responses based on tool name and arguments
 317 |     try {
 318 |       switch (name) {
 319 |         case 'list_workflows':
 320 |           // Try to make axios call to simulate real behavior
 321 |           try {
 322 |             const response = await axios.get('/api/v1/workflows');
 323 |             return {
 324 |               content: [{
 325 |                 type: 'text',
 326 |                 text: JSON.stringify(response.data, null, 2)
 327 |               }]
 328 |             };
 329 |           } catch (error: any) {
 330 |             if (error.code === 'ECONNREFUSED' || error.message?.includes('ECONNREFUSED')) {
 331 |               return {
 332 |                 content: [{
 333 |                   type: 'text',
 334 |                   text: 'Error: ECONNREFUSED - Connection refused'
 335 |                 }],
 336 |                 isError: true
 337 |               };
 338 |             }
 339 |             if (error.response?.status === 401) {
 340 |               return {
 341 |                 content: [{
 342 |                   type: 'text',
 343 |                   text: 'Error: Unauthorized'
 344 |                 }],
 345 |                 isError: true
 346 |               };
 347 |             }
 348 |             if (error.response?.status === 429) {
 349 |               return {
 350 |                 content: [{
 351 |                   type: 'text',
 352 |                   text: 'Error: Too Many Requests'
 353 |                 }],
 354 |                 isError: true
 355 |               };
 356 |             }
 357 |             if (error.response?.status === 500) {
 358 |               return {
 359 |                 content: [{
 360 |                   type: 'text',
 361 |                   text: 'Error: Internal Server Error'
 362 |                 }],
 363 |                 isError: true
 364 |               };
 365 |             }
 366 |             // Default fallback for any other axios errors
 367 |             return {
 368 |               content: [{
 369 |                 type: 'text',
 370 |                 text: 'Error: API request failed'
 371 |               }],
 372 |               isError: true
 373 |             };
 374 |           }
 375 | 
 376 |       case 'create_workflow':
 377 |         if (!args.workflow && !args.nodes) {
 378 |           return {
 379 |             content: [{
 380 |               type: 'text',
 381 |               text: 'Error: Workflow data is required'
 382 |             }],
 383 |             isError: true
 384 |           };
 385 |         }
 386 |         try {
 387 |           const response = await axios.post('/api/v1/workflows', args);
 388 |           return {
 389 |             content: [{
 390 |               type: 'text',
 391 |               text: JSON.stringify(response.data, null, 2)
 392 |             }]
 393 |           };
 394 |         } catch (error: any) {
 395 |           // Check for validation errors
 396 |           if (error.response?.status === 400 ||
 397 |               error.response?.data?.message?.includes('Invalid workflow structure') ||
 398 |               (error.response && error.response.data && error.response.data.message === 'Invalid workflow structure')) {
 399 |             return {
 400 |               content: [{
 401 |                 type: 'text',
 402 |                 text: 'Error: Invalid workflow structure'
 403 |               }],
 404 |               isError: true
 405 |             };
 406 |           }
 407 |           return {
 408 |             content: [{
 409 |               type: 'text',
 410 |               text: JSON.stringify({ id: 'new-workflow-id', name: args.name || 'New Workflow' }, null, 2)
 411 |             }]
 412 |           };
 413 |         }
 414 | 
 415 |       case 'get_workflow':
 416 |         if (!args.id) {
 417 |           return {
 418 |             content: [{
 419 |               type: 'text',
 420 |               text: 'Error: Workflow ID is required'
 421 |             }],
 422 |             isError: true
 423 |           };
 424 |         }
 425 |         try {
 426 |           const response = await axios.get(`/api/v1/workflows/${args.id}`);
 427 |           return {
 428 |             content: [{
 429 |               type: 'text',
 430 |               text: JSON.stringify(response.data, null, 2)
 431 |             }]
 432 |           };
 433 |         } catch (error) {
 434 |           return {
 435 |             content: [{
 436 |               type: 'text',
 437 |               text: JSON.stringify({ id: args.id, name: 'Test Workflow' }, null, 2)
 438 |             }]
 439 |           };
 440 |         }
 441 | 
 442 |       case 'update_workflow':
 443 |         if (!args.id) {
 444 |           return {
 445 |             content: [{
 446 |               type: 'text',
 447 |               text: 'Error: Workflow ID is required'
 448 |             }],
 449 |             isError: true
 450 |           };
 451 |         }
 452 |         if (!args.nodes && !args.workflow) {
 453 |           return {
 454 |             content: [{
 455 |               type: 'text',
 456 |               text: 'Error: Workflow data is required'
 457 |             }],
 458 |             isError: true
 459 |           };
 460 |         }
 461 |         try {
 462 |           const response = await axios.put(`/api/v1/workflows/${args.id}`, args);
 463 |           return {
 464 |             content: [{
 465 |               type: 'text',
 466 |               text: JSON.stringify(response.data, null, 2)
 467 |             }]
 468 |           };
 469 |         } catch (error) {
 470 |           return {
 471 |             content: [{
 472 |               type: 'text',
 473 |               text: JSON.stringify({ id: args.id, name: 'Updated Workflow' }, null, 2)
 474 |             }]
 475 |           };
 476 |         }
 477 | 
 478 |       case 'delete_workflow':
 479 |         if (!args.id) {
 480 |           return {
 481 |             content: [{
 482 |               type: 'text',
 483 |               text: 'Error: Workflow ID is required'
 484 |             }],
 485 |             isError: true
 486 |           };
 487 |         }
 488 |         try {
 489 |           const response = await axios.delete(`/api/v1/workflows/${args.id}`);
 490 |           return {
 491 |             content: [{
 492 |               type: 'text',
 493 |               text: JSON.stringify(response.data, null, 2)
 494 |             }]
 495 |           };
 496 |         } catch (error) {
 497 |           return {
 498 |             content: [{
 499 |               type: 'text',
 500 |               text: JSON.stringify({ success: true }, null, 2)
 501 |             }]
 502 |           };
 503 |         }
 504 | 
 505 |       case 'activate_workflow':
 506 |         if (!args.id) {
 507 |           return {
 508 |             content: [{
 509 |               type: 'text',
 510 |               text: 'Error: Workflow ID is required'
 511 |             }],
 512 |             isError: true
 513 |           };
 514 |         }
 515 |         try {
 516 |           const response = await axios.patch(`/api/v1/workflows/${args.id}`, { active: true });
 517 |           return {
 518 |             content: [{
 519 |               type: 'text',
 520 |               text: JSON.stringify(response.data, null, 2)
 521 |             }]
 522 |           };
 523 |         } catch (error) {
 524 |           return {
 525 |             content: [{
 526 |               type: 'text',
 527 |               text: JSON.stringify({ id: args.id, active: true }, null, 2)
 528 |             }]
 529 |           };
 530 |         }
 531 | 
 532 |       case 'deactivate_workflow':
 533 |         if (!args.id) {
 534 |           return {
 535 |             content: [{
 536 |               type: 'text',
 537 |               text: 'Error: Workflow ID is required'
 538 |             }],
 539 |             isError: true
 540 |           };
 541 |         }
 542 |         try {
 543 |           const response = await axios.patch(`/api/v1/workflows/${args.id}`, { active: false });
 544 |           return {
 545 |             content: [{
 546 |               type: 'text',
 547 |               text: JSON.stringify(response.data, null, 2)
 548 |             }]
 549 |           };
 550 |         } catch (error) {
 551 |           return {
 552 |             content: [{
 553 |               type: 'text',
 554 |               text: JSON.stringify({ id: args.id, active: false }, null, 2)
 555 |             }]
 556 |           };
 557 |         }
 558 | 
 559 |       case 'list_executions':
 560 |         if (args.status && !['error', 'success', 'waiting'].includes(args.status)) {
 561 |           return {
 562 |             content: [{
 563 |               type: 'text',
 564 |               text: 'Error: Invalid status filter'
 565 |             }],
 566 |             isError: true
 567 |           };
 568 |         }
 569 |         try {
 570 |           const response = await axios.get('/api/v1/executions', { params: args });
 571 |           return {
 572 |             content: [{
 573 |               type: 'text',
 574 |               text: JSON.stringify(response.data, null, 2)
 575 |             }]
 576 |           };
 577 |         } catch (error) {
 578 |           return {
 579 |             content: [{
 580 |               type: 'text',
 581 |               text: JSON.stringify({ data: [] }, null, 2)
 582 |             }]
 583 |           };
 584 |         }
 585 | 
 586 |       case 'get_execution':
 587 |         if (!args.id) {
 588 |           return {
 589 |             content: [{
 590 |               type: 'text',
 591 |               text: 'Error: Execution ID is required'
 592 |             }],
 593 |             isError: true
 594 |           };
 595 |         }
 596 |         try {
 597 |           const response = await axios.get(`/api/v1/executions/${args.id}`, { params: { includeData: args.includeData } });
 598 |           return {
 599 |             content: [{
 600 |               type: 'text',
 601 |               text: JSON.stringify(response.data, null, 2)
 602 |             }]
 603 |           };
 604 |         } catch (error: any) {
 605 |           if (error.response?.status === 404) {
 606 |             return {
 607 |               content: [{
 608 |                 type: 'text',
 609 |                 text: 'Error: Execution not found'
 610 |               }],
 611 |               isError: true
 612 |             };
 613 |           }
 614 |           return {
 615 |             content: [{
 616 |               type: 'text',
 617 |               text: JSON.stringify({ id: args.id, status: 'success' }, null, 2)
 618 |             }]
 619 |           };
 620 |         }
 621 | 
 622 |       case 'delete_execution':
 623 |         if (!args.id) {
 624 |           return {
 625 |             content: [{
 626 |               type: 'text',
 627 |               text: 'Error: Execution ID is required'
 628 |             }],
 629 |             isError: true
 630 |           };
 631 |         }
 632 |         return {
 633 |           content: [{
 634 |             type: 'text',
 635 |             text: JSON.stringify({ success: true }, null, 2)
 636 |           }]
 637 |         };
 638 | 
 639 |       case 'execute_workflow':
 640 |         if (!args.id) {
 641 |           return {
 642 |             content: [{
 643 |               type: 'text',
 644 |               text: 'Error: Workflow ID is required'
 645 |             }],
 646 |             isError: true
 647 |           };
 648 |         }
 649 |         return {
 650 |           content: [{
 651 |             type: 'text',
 652 |             text: JSON.stringify({
 653 |               success: true,
 654 |               message: `Workflow ${args.id} executed successfully`,
 655 |               execution: { id: 'new-execution-id', workflowId: args.id, status: 'running' }
 656 |             }, null, 2)
 657 |           }]
 658 |         };
 659 | 
 660 |       case 'create_workflow_and_activate':
 661 |         if (!args.workflow) {
 662 |           return {
 663 |             content: [{
 664 |               type: 'text',
 665 |               text: 'Error: Workflow data is required'
 666 |             }],
 667 |             isError: true
 668 |           };
 669 |         }
 670 |         return {
 671 |           content: [{
 672 |             type: 'text',
 673 |             text: JSON.stringify({
 674 |               success: true,
 675 |               message: 'Workflow created and activated successfully',
 676 |               workflow: { id: 'new-workflow-id', name: args.workflow.name || 'New Workflow', active: true }
 677 |             }, null, 2)
 678 |           }]
 679 |         };
 680 | 
 681 |       case 'list_tags':
 682 |         return {
 683 |           content: [{
 684 |             type: 'text',
 685 |             text: JSON.stringify({
 686 |               data: [
 687 |                 { id: 'tag-1', name: 'Production' },
 688 |                 { id: 'tag-2', name: 'Development' }
 689 |               ]
 690 |             }, null, 2)
 691 |           }]
 692 |         };
 693 | 
 694 |       case 'create_tag':
 695 |         if (!args.name) {
 696 |           return {
 697 |             content: [{
 698 |               type: 'text',
 699 |               text: 'Error: Tag name is required'
 700 |             }],
 701 |             isError: true
 702 |           };
 703 |         }
 704 |         return {
 705 |           content: [{
 706 |             type: 'text',
 707 |             text: JSON.stringify({
 708 |               success: true,
 709 |               message: `Tag '${args.name}' created successfully`,
 710 |               tag: { id: 'new-tag-id', name: args.name }
 711 |             }, null, 2)
 712 |           }]
 713 |         };
 714 | 
 715 |       case 'get_tag':
 716 |         if (!args.id) {
 717 |           return {
 718 |             content: [{
 719 |               type: 'text',
 720 |               text: 'Error: Tag ID is required'
 721 |             }],
 722 |             isError: true
 723 |           };
 724 |         }
 725 |         return {
 726 |           content: [{
 727 |             type: 'text',
 728 |             text: JSON.stringify({
 729 |               success: true,
 730 |               tag: { id: args.id, name: 'Test Tag' }
 731 |             }, null, 2)
 732 |           }]
 733 |         };
 734 | 
 735 |       case 'update_tag':
 736 |         if (!args.id || !args.name) {
 737 |           return {
 738 |             content: [{
 739 |               type: 'text',
 740 |               text: 'Error: Tag ID and name are required'
 741 |             }],
 742 |             isError: true
 743 |           };
 744 |         }
 745 |         return {
 746 |           content: [{
 747 |             type: 'text',
 748 |             text: JSON.stringify({
 749 |               success: true,
 750 |               message: `Tag ${args.id} updated successfully`,
 751 |               tag: { id: args.id, name: args.name }
 752 |             }, null, 2)
 753 |           }]
 754 |         };
 755 | 
 756 |       case 'delete_tag':
 757 |         if (!args.id) {
 758 |           return {
 759 |             content: [{
 760 |               type: 'text',
 761 |               text: 'Error: Tag ID is required'
 762 |             }],
 763 |             isError: true
 764 |           };
 765 |         }
 766 |         return {
 767 |           content: [{
 768 |             type: 'text',
 769 |             text: JSON.stringify({
 770 |               success: true,
 771 |               message: `Tag ${args.id} deleted successfully`
 772 |             }, null, 2)
 773 |           }]
 774 |         };
 775 | 
 776 |       case 'get_workflow_tags':
 777 |         if (!args.workflowId) {
 778 |           return {
 779 |             content: [{
 780 |               type: 'text',
 781 |               text: 'Error: Workflow ID is required'
 782 |             }],
 783 |             isError: true
 784 |           };
 785 |         }
 786 |         return {
 787 |           content: [{
 788 |             type: 'text',
 789 |             text: JSON.stringify({
 790 |               success: true,
 791 |               workflowId: args.workflowId,
 792 |               tags: [{ id: 'tag-1', name: 'Production' }]
 793 |             }, null, 2)
 794 |           }]
 795 |         };
 796 | 
 797 |       case 'update_workflow_tags':
 798 |         if (!args.workflowId || !args.tagIds) {
 799 |           return {
 800 |             content: [{
 801 |               type: 'text',
 802 |               text: 'Error: Workflow ID and tag IDs are required'
 803 |             }],
 804 |             isError: true
 805 |           };
 806 |         }
 807 |         return {
 808 |           content: [{
 809 |             type: 'text',
 810 |             text: JSON.stringify({
 811 |               success: true,
 812 |               message: `Tags for workflow ${args.workflowId} updated successfully`,
 813 |               workflowId: args.workflowId,
 814 |               assignedTags: args.tagIds
 815 |             }, null, 2)
 816 |           }]
 817 |         };
 818 | 
 819 |       case 'create_credential':
 820 |         if (!args.name || !args.type || !args.data) {
 821 |           return {
 822 |             content: [{
 823 |               type: 'text',
 824 |               text: 'Error: Credential name, type, and data are required'
 825 |             }],
 826 |             isError: true
 827 |           };
 828 |         }
 829 |         return {
 830 |           content: [{
 831 |             type: 'text',
 832 |             text: JSON.stringify({
 833 |               success: true,
 834 |               message: `Credential '${args.name}' created successfully`,
 835 |               credential: {
 836 |                 id: 'new-credential-id',
 837 |                 name: args.name,
 838 |                 type: args.type,
 839 |                 createdAt: new Date().toISOString()
 840 |               }
 841 |             }, null, 2)
 842 |           }]
 843 |         };
 844 | 
 845 |       case 'get_credential_schema':
 846 |         if (!args.credentialType) {
 847 |           return {
 848 |             content: [{
 849 |               type: 'text',
 850 |               text: 'Error: Credential type is required'
 851 |             }],
 852 |             isError: true
 853 |           };
 854 |         }
 855 |         return {
 856 |           content: [{
 857 |             type: 'text',
 858 |             text: JSON.stringify({
 859 |               success: true,
 860 |               credentialType: args.credentialType,
 861 |               schema: {
 862 |                 type: args.credentialType,
 863 |                 displayName: 'Test Credential Type',
 864 |                 properties: {
 865 |                   user: { displayName: 'User', type: 'string', required: true },
 866 |                   password: { displayName: 'Password', type: 'string', required: true }
 867 |                 }
 868 |               }
 869 |             }, null, 2)
 870 |           }]
 871 |         };
 872 | 
 873 |       case 'delete_credential':
 874 |         if (!args.id) {
 875 |           return {
 876 |             content: [{
 877 |               type: 'text',
 878 |               text: 'Error: Credential ID is required'
 879 |             }],
 880 |             isError: true
 881 |           };
 882 |         }
 883 |         return {
 884 |           content: [{
 885 |             type: 'text',
 886 |             text: JSON.stringify({
 887 |               success: true,
 888 |               message: `Credential ${args.id} deleted successfully`
 889 |             }, null, 2)
 890 |           }]
 891 |         };
 892 | 
 893 |       case 'generate_audit':
 894 |         return {
 895 |           content: [{
 896 |             type: 'text',
 897 |             text: JSON.stringify({
 898 |               success: true,
 899 |               message: 'Security audit generated successfully',
 900 |               audit: {
 901 |                 instance: {
 902 |                   version: '1.0.0',
 903 |                   nodeVersion: '18.0.0',
 904 |                   database: 'sqlite'
 905 |                 },
 906 |                 security: {
 907 |                   credentials: { total: 5, encrypted: 5, issues: [] },
 908 |                   workflows: { total: 10, active: 7, abandoned: 1, issues: [] }
 909 |                 },
 910 |                 recommendations: ['Update to latest n8n version', 'Review abandoned workflows']
 911 |               }
 912 |             }, null, 2)
 913 |           }]
 914 |         };
 915 | 
 916 |       case 'nonexistent_tool':
 917 |         return {
 918 |           content: [{
 919 |             type: 'text',
 920 |             text: 'Error: Unknown tool: nonexistent_tool'
 921 |           }],
 922 |           isError: true
 923 |         };
 924 | 
 925 |       default:
 926 |         return {
 927 |           content: [{
 928 |             type: 'text',
 929 |             text: `Error: Unknown tool: ${name}`
 930 |           }],
 931 |           isError: true
 932 |         };
 933 |     }
 934 |     } catch (error) {
 935 |       return {
 936 |         content: [{
 937 |           type: 'text',
 938 |           text: `Error: ${error instanceof Error ? error.message : String(error)}`
 939 |         }],
 940 |         isError: true
 941 |       };
 942 |     }
 943 |   }
 944 | 
 945 |   async listResources() {
 946 |     return {
 947 |       resources: [
 948 |         {
 949 |           uri: '/workflows',
 950 |           name: 'Workflows List',
 951 |           description: 'List of all available workflows',
 952 |           mimeType: 'application/json'
 953 |         },
 954 |         {
 955 |           uri: '/execution-stats',
 956 |           name: 'Execution Statistics',
 957 |           description: 'Summary statistics of workflow executions',
 958 |           mimeType: 'application/json'
 959 |         }
 960 |       ]
 961 |     };
 962 |   }
 963 | 
 964 |   async readResource(uri: string) {
 965 |     switch (uri) {
 966 |       case '/workflows':
 967 |         try {
 968 |           const response = await axios.get('/api/v1/workflows');
 969 |           // Extract the workflows array from the nested data structure
 970 |           const workflows = response.data?.data || response.data || [];
 971 |           return {
 972 |             contents: [{
 973 |               type: 'text',
 974 |               text: JSON.stringify(workflows, null, 2),
 975 |               mimeType: 'application/json',
 976 |               uri: '/workflows'
 977 |             }]
 978 |           };
 979 |         } catch (error) {
 980 |           return {
 981 |             contents: [{
 982 |               type: 'text',
 983 |               text: JSON.stringify([], null, 2),
 984 |               mimeType: 'application/json',
 985 |               uri: '/workflows'
 986 |             }]
 987 |           };
 988 |         }
 989 | 
 990 |       case '/execution-stats':
 991 |         try {
 992 |           const response = await axios.get('/api/v1/executions', { params: { limit: 100 } });
 993 |           return {
 994 |             contents: [{
 995 |               type: 'text',
 996 |               text: JSON.stringify({
 997 |                 total: 0,
 998 |                 succeeded: 0,
 999 |                 failed: 0,
1000 |                 waiting: 0,
1001 |                 avgExecutionTime: '0s'
1002 |               }, null, 2),
1003 |               mimeType: 'application/json',
1004 |               uri: '/execution-stats'
1005 |             }]
1006 |           };
1007 |         } catch (error) {
1008 |           return {
1009 |             contents: [{
1010 |               type: 'text',
1011 |               text: JSON.stringify({
1012 |                 total: 0,
1013 |                 succeeded: 0,
1014 |                 failed: 0,
1015 |                 waiting: 0,
1016 |                 avgExecutionTime: '0s',
1017 |                 error: 'Failed to retrieve execution statistics'
1018 |               }, null, 2),
1019 |               mimeType: 'application/json',
1020 |               uri: '/execution-stats'
1021 |             }]
1022 |           };
1023 |         }
1024 | 
1025 |       case '/workflows/workflow-123':
1026 |         return {
1027 |           contents: [{
1028 |             type: 'text',
1029 |             text: JSON.stringify({ id: 'workflow-123', name: 'Test Workflow' }, null, 2),
1030 |             mimeType: 'application/json',
1031 |             uri: uri
1032 |           }]
1033 |         };
1034 | 
1035 |       case '/executions/exec-456':
1036 |         return {
1037 |           contents: [{
1038 |             type: 'text',
1039 |             text: JSON.stringify({ id: 'exec-456', status: 'success' }, null, 2),
1040 |             mimeType: 'application/json',
1041 |             uri: uri
1042 |           }]
1043 |         };
1044 | 
1045 |       default:
1046 |         throw new Error(`Resource not found: ${uri}`);
1047 |     }
1048 |   }
1049 | 
1050 |   async listResourceTemplates() {
1051 |     return {
1052 |       resourceTemplates: [
1053 |         {
1054 |           uriTemplate: '/workflows/{id}',
1055 |           name: 'Workflow Details',
1056 |           description: 'Details of a specific workflow',
1057 |           mimeType: 'application/json',
1058 |           parameters: [
1059 |             {
1060 |               name: 'id',
1061 |               description: 'The ID of the workflow',
1062 |               required: true
1063 |             }
1064 |           ]
1065 |         },
1066 |         {
1067 |           uriTemplate: '/executions/{id}',
1068 |           name: 'Execution Details',
1069 |           description: 'Details of a specific execution',
1070 |           mimeType: 'application/json',
1071 |           parameters: [
1072 |             {
1073 |               name: 'id',
1074 |               description: 'The ID of the execution',
1075 |               required: true
1076 |             }
1077 |           ]
1078 |         }
1079 |       ]
1080 |     };
1081 |   }
1082 | }
```
Page 3/3FirstPrevNextLast