Skip to main content
Glama

n8n-MCP

by 88-888
ai-agent-validation.test.ts13.8 kB
/** * Integration Tests: AI Agent Validation * * Tests AI Agent validation against real n8n instance. * These tests validate the fixes from v2.17.0 including node type normalization. */ import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest'; import { createTestContext, TestContext, createTestWorkflowName } from '../n8n-api/utils/test-context'; import { getTestN8nClient } from '../n8n-api/utils/n8n-client'; import { N8nApiClient } from '../../../src/services/n8n-api-client'; import { cleanupOrphanedWorkflows } from '../n8n-api/utils/cleanup-helpers'; import { createMcpContext } from '../n8n-api/utils/mcp-context'; import { InstanceContext } from '../../../src/types/instance-context'; import { handleValidateWorkflow } from '../../../src/mcp/handlers-n8n-manager'; import { getNodeRepository, closeNodeRepository } from '../n8n-api/utils/node-repository'; import { NodeRepository } from '../../../src/database/node-repository'; import { ValidationResponse } from '../n8n-api/types/mcp-responses'; import { createAIAgentNode, createChatTriggerNode, createLanguageModelNode, createHTTPRequestToolNode, createCodeToolNode, createMemoryNode, createRespondNode, createAIConnection, createMainConnection, mergeConnections, createAIWorkflow } from './helpers'; describe('Integration: AI Agent Validation', () => { let context: TestContext; let client: N8nApiClient; let mcpContext: InstanceContext; let repository: NodeRepository; beforeEach(async () => { context = createTestContext(); client = getTestN8nClient(); mcpContext = createMcpContext(); repository = await getNodeRepository(); }); afterEach(async () => { await context.cleanup(); }); afterAll(async () => { await closeNodeRepository(); if (!process.env.CI) { await cleanupOrphanedWorkflows(); } }); // ====================================================================== // TEST 1: Missing Language Model // ====================================================================== it('should detect missing language model in real workflow', async () => { const agent = createAIAgentNode({ name: 'AI Agent', text: 'Test prompt' }); const workflow = createAIWorkflow( [agent], {}, { name: createTestWorkflowName('AI Agent - Missing Model'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(false); expect(data.errors).toBeDefined(); expect(data.errors!.length).toBeGreaterThan(0); const errorCodes = data.errors!.map(e => e.details?.code || e.code); expect(errorCodes).toContain('MISSING_LANGUAGE_MODEL'); const errorMessages = data.errors!.map(e => e.message).join(' '); expect(errorMessages).toMatch(/language model|ai_languageModel/i); }); // ====================================================================== // TEST 2: Valid AI Agent with Language Model // ====================================================================== it('should validate AI Agent with language model', async () => { const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const agent = createAIAgentNode({ name: 'AI Agent', text: 'You are a helpful assistant' }); const workflow = createAIWorkflow( [languageModel, agent], mergeConnections( createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel') ), { name: createTestWorkflowName('AI Agent - Valid'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(true); expect(data.errors).toBeUndefined(); expect(data.summary.errorCount).toBe(0); }); // ====================================================================== // TEST 3: Tool Connections Detection // ====================================================================== it('should detect tool connections correctly', async () => { const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const httpTool = createHTTPRequestToolNode({ name: 'HTTP Request Tool', toolDescription: 'Fetches weather data from API', url: 'https://api.weather.com/current', method: 'GET' }); const agent = createAIAgentNode({ name: 'AI Agent', text: 'You are a weather assistant' }); const workflow = createAIWorkflow( [languageModel, httpTool, agent], mergeConnections( createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'), createAIConnection('HTTP Request Tool', 'AI Agent', 'ai_tool') ), { name: createTestWorkflowName('AI Agent - With Tool'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(true); // Should NOT have false "no tools" warning if (data.warnings) { const toolWarnings = data.warnings.filter(w => w.message.toLowerCase().includes('no ai_tool') ); expect(toolWarnings.length).toBe(0); } }); // ====================================================================== // TEST 4: Streaming Mode Constraints (Chat Trigger) // ====================================================================== it('should validate streaming mode constraints', async () => { const chatTrigger = createChatTriggerNode({ name: 'Chat Trigger', responseMode: 'streaming' }); const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const agent = createAIAgentNode({ name: 'AI Agent', text: 'You are a helpful assistant' }); const respond = createRespondNode({ name: 'Respond to Webhook' }); const workflow = createAIWorkflow( [chatTrigger, languageModel, agent, respond], mergeConnections( createMainConnection('Chat Trigger', 'AI Agent'), createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'), createMainConnection('AI Agent', 'Respond to Webhook') // ERROR: streaming with main output ), { name: createTestWorkflowName('AI Agent - Streaming Error'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(false); expect(data.errors).toBeDefined(); const streamingErrors = data.errors!.filter(e => { const code = e.details?.code || e.code; return code === 'STREAMING_WITH_MAIN_OUTPUT' || code === 'STREAMING_AGENT_HAS_OUTPUT'; }); expect(streamingErrors.length).toBeGreaterThan(0); }); // ====================================================================== // TEST 5: AI Agent Own streamResponse Setting // ====================================================================== it('should validate AI Agent own streamResponse setting', async () => { const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const agent = createAIAgentNode({ name: 'AI Agent', text: 'You are a helpful assistant', streamResponse: true // Agent has its own streaming enabled }); const respond = createRespondNode({ name: 'Respond to Webhook' }); const workflow = createAIWorkflow( [languageModel, agent, respond], mergeConnections( createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'), createMainConnection('AI Agent', 'Respond to Webhook') // ERROR: streaming with main output ), { name: createTestWorkflowName('AI Agent - Own Streaming'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(false); expect(data.errors).toBeDefined(); const errorCodes = data.errors!.map(e => e.details?.code || e.code); expect(errorCodes).toContain('STREAMING_WITH_MAIN_OUTPUT'); }); // ====================================================================== // TEST 6: Multiple Memory Connections // ====================================================================== it('should validate memory connections', async () => { const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const memory1 = createMemoryNode({ name: 'Memory 1' }); const memory2 = createMemoryNode({ name: 'Memory 2' }); const agent = createAIAgentNode({ name: 'AI Agent', text: 'You are a helpful assistant' }); const workflow = createAIWorkflow( [languageModel, memory1, memory2, agent], mergeConnections( createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'), createAIConnection('Memory 1', 'AI Agent', 'ai_memory'), createAIConnection('Memory 2', 'AI Agent', 'ai_memory') // ERROR: multiple memory ), { name: createTestWorkflowName('AI Agent - Multiple Memory'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(false); expect(data.errors).toBeDefined(); const errorCodes = data.errors!.map(e => e.details?.code || e.code); expect(errorCodes).toContain('MULTIPLE_MEMORY_CONNECTIONS'); }); // ====================================================================== // TEST 7: Complete AI Workflow (All Components) // ====================================================================== it('should validate complete AI workflow', async () => { const chatTrigger = createChatTriggerNode({ name: 'Chat Trigger', responseMode: 'lastNode' // Not streaming }); const languageModel = createLanguageModelNode('openai', { name: 'OpenAI Chat Model' }); const httpTool = createHTTPRequestToolNode({ name: 'HTTP Request Tool', toolDescription: 'Fetches data from external API', url: 'https://api.example.com/data', method: 'GET' }); const codeTool = createCodeToolNode({ name: 'Code Tool', toolDescription: 'Processes data with custom logic', code: 'return { result: "processed" };' }); const memory = createMemoryNode({ name: 'Window Buffer Memory', contextWindowLength: 5 }); const agent = createAIAgentNode({ name: 'AI Agent', promptType: 'define', text: 'You are a helpful assistant with access to tools', systemMessage: 'You are an AI assistant that helps users with data processing and external API calls.' }); const respond = createRespondNode({ name: 'Respond to Webhook' }); const workflow = createAIWorkflow( [chatTrigger, languageModel, httpTool, codeTool, memory, agent, respond], mergeConnections( createMainConnection('Chat Trigger', 'AI Agent'), createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'), createAIConnection('HTTP Request Tool', 'AI Agent', 'ai_tool'), createAIConnection('Code Tool', 'AI Agent', 'ai_tool'), createAIConnection('Window Buffer Memory', 'AI Agent', 'ai_memory'), createMainConnection('AI Agent', 'Respond to Webhook') ), { name: createTestWorkflowName('AI Agent - Complete Workflow'), tags: ['mcp-integration-test', 'ai-validation'] } ); const created = await client.createWorkflow(workflow); context.trackWorkflow(created.id!); const response = await handleValidateWorkflow( { id: created.id }, repository, mcpContext ); expect(response.success).toBe(true); const data = response.data as ValidationResponse; expect(data.valid).toBe(true); expect(data.errors).toBeUndefined(); expect(data.summary.errorCount).toBe(0); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/88-888/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server