import { nanoid } from 'nanoid';

import { test, expect } from '../../../fixtures/base';
import type { McpSession } from '../../../services/mcp-api-helper';

/**
 * E2E tests for the MCP Server Trigger node.
 *
 * Tests cover:
 * - SSE and Streamable HTTP transports
 * - Authentication (none, bearer, header)
 * - Tool listing and execution
 * - Session management
 * - Error handling
 */

test.describe(
	'MCP Trigger Node',
	{
		annotation: [{ type: 'owner', description: 'AI' }],
	},
	() => {
		test.describe('Streamable HTTP Transport', () => {
			test('should initialize session and return mcp-session-id', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				// Get the MCP path from the workflow
				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);

				expect(session.sessionId).toBeTruthy();
				expect(session.transport).toBe('streamableHttp');
			});

			test('should list tools via Streamable HTTP', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);
				const tools = await api.mcp.listTools(session, mcpPath);

				expect(tools).toHaveLength(1);
				expect(tools[0].name).toBe('echo');
				expect(tools[0].description).toContain('Echoes');
			});

			test('should call tool via Streamable HTTP', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);
				const result = await api.mcp.callTool(session, mcpPath, 'echo', {
					message: 'Hello from E2E test!',
				});

				expect(result.content).toBeDefined();
				expect(result.content.length).toBeGreaterThan(0);
				expect(result.content[0].text).toContain('Hello from E2E test!');
			});

			test('should close session via DELETE', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);
				const deleteResponse = await api.mcp.streamableHttpDelete(session, mcpPath);

				// DELETE should return success (200 or 202)
				expect(deleteResponse.status()).toBeLessThan(300);
			});
		});

		test.describe('SSE Transport', () => {
			test('should establish SSE connection and return session', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.sseSetup(mcpPath);

				try {
					expect(session.sessionId).toBeTruthy();
					expect(session.transport).toBe('sse');
					expect(session.postUrl).toBeTruthy();
				} finally {
					api.mcp.sseClose(session);
				}
			});

			test('should list connected tools via SSE', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.sseSetup(mcpPath);

				try {
					const tools = await api.mcp.listTools(session, mcpPath);

					expect(tools).toHaveLength(1);
					expect(tools[0].name).toBe('echo');
				} finally {
					api.mcp.sseClose(session);
				}
			});

			test('should call tool and receive response via SSE', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.sseSetup(mcpPath);

				try {
					const result = await api.mcp.callTool(session, mcpPath, 'echo', {
						message: 'SSE test message',
					});

					expect(result.content).toBeDefined();
					expect(result.content[0].text).toContain('SSE test message');
				} finally {
					api.mcp.sseClose(session);
				}
			});
		});

		test.describe('Authentication', () => {
			test('should reject unauthenticated request with bearerAuth', async ({ api }) => {
				const token = `secret-token-${nanoid()}`;
				const credential = await api.credentials.createCredential({
					type: 'httpBearerAuth',
					name: `mcp-bearer-${nanoid()}`,
					data: { token },
				});

				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-bearer-auth.json',
					{
						transform: (wf) => {
							const mcpNode = wf.nodes?.find((n) => n.type.includes('mcpTrigger'));
							if (mcpNode) {
								mcpNode.credentials = {
									httpBearerAuth: { id: credential.id, name: credential.name },
								};
							}
							return wf;
						},
					},
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// Try without auth - should fail
				const noAuthResponse = await api.webhooks.trigger(mcpPath, {
					method: 'POST',
					headers: { 'Content-Type': 'application/json' },
					data: api.mcp.createMessage('initialize', {
						protocolVersion: '2024-11-05',
						capabilities: {},
						clientInfo: { name: 'test', version: '1.0.0' },
					}),
				});

				expect(noAuthResponse.status()).toBe(403);
			});

			test('should accept valid bearer token', async ({ api }) => {
				const token = `secret-token-${nanoid()}`;
				const credential = await api.credentials.createCredential({
					type: 'httpBearerAuth',
					name: `mcp-bearer-${nanoid()}`,
					data: { token },
				});

				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-bearer-auth.json',
					{
						transform: (wf) => {
							const mcpNode = wf.nodes?.find((n) => n.type.includes('mcpTrigger'));
							if (mcpNode) {
								mcpNode.credentials = {
									httpBearerAuth: { id: credential.id, name: credential.name },
								};
							}
							return wf;
						},
					},
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// Try with valid auth - should succeed
				const session = await api.mcp.streamableHttpInitialize(mcpPath, {
					headers: { Authorization: `Bearer ${token}` },
				});

				expect(session.sessionId).toBeTruthy();
			});

			test('should accept valid header auth', async ({ api }) => {
				const headerName = `X-Auth-${nanoid(8)}`;
				const headerValue = `secret-value-${nanoid()}`;
				const credential = await api.credentials.createCredential({
					type: 'httpHeaderAuth',
					name: `mcp-header-${nanoid()}`,
					data: { name: headerName, value: headerValue },
				});

				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-header-auth.json',
					{
						transform: (wf) => {
							const mcpNode = wf.nodes?.find((n) => n.type.includes('mcpTrigger'));
							if (mcpNode) {
								mcpNode.credentials = {
									httpHeaderAuth: { id: credential.id, name: credential.name },
								};
							}
							return wf;
						},
					},
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// Try without auth - should fail
				const noAuthResponse = await api.webhooks.trigger(mcpPath, {
					method: 'POST',
					headers: { 'Content-Type': 'application/json' },
					data: api.mcp.createMessage('initialize', {
						protocolVersion: '2024-11-05',
						capabilities: {},
						clientInfo: { name: 'test', version: '1.0.0' },
					}),
				});
				expect(noAuthResponse.status()).toBe(403);

				// Try with valid auth - should succeed
				const session = await api.mcp.streamableHttpInitialize(mcpPath, {
					headers: { [headerName]: headerValue },
				});

				expect(session.sessionId).toBeTruthy();
			});
		});

		test.describe('Tool Operations', () => {
			test('should return all connected tools in tools/list', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-multi-tool.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);
				const tools = await api.mcp.listTools(session, mcpPath);

				expect(tools).toHaveLength(3);

				const toolNames = tools.map((t) => t.name).sort();
				expect(toolNames).toEqual(['add', 'echo', 'multiply']);
			});

			test('should execute tool with arguments', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-multi-tool.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);

				// Test echo tool
				const echoResult = await api.mcp.callTool(session, mcpPath, 'echo', {
					message: 'Multi-tool test',
				});
				expect(echoResult.content[0].text).toContain('Multi-tool test');

				// Test add tool
				const addResult = await api.mcp.callTool(session, mcpPath, 'add', { a: 5, b: 3 });
				expect(addResult.content[0].text).toContain('8');

				// Test multiply tool
				const multiplyResult = await api.mcp.callTool(session, mcpPath, 'multiply', { a: 4, b: 7 });
				expect(multiplyResult.content[0].text).toContain('28');
			});

			test('should return error for unknown tool', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				const session = await api.mcp.streamableHttpInitialize(mcpPath);

				// Try to call a non-existent tool
				const message = api.mcp.createMessage('tools/call', {
					name: 'nonexistent_tool',
					arguments: {},
				});

				const response = await api.mcp.streamableHttpSendMessage(session, mcpPath, message);
				const body = await response.text();

				// Should get an error response
				expect(body).toContain('error');
			});
		});

		test.describe('Session Management', () => {
			test('should reject requests with invalid session ID', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// Create a fake session with an invalid session ID
				const fakeSession: McpSession = {
					sessionId: 'invalid-session-id-12345',
					transport: 'streamableHttp',
				};

				const message = api.mcp.createMessage('tools/list');
				const response = await api.mcp.streamableHttpSendMessage(fakeSession, mcpPath, message);

				// Should return an error status (404 or 401)
				expect(response.status()).toBeGreaterThanOrEqual(400);
			});

			test('should cleanup session on DELETE request', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// Initialize and then delete session
				const session = await api.mcp.streamableHttpInitialize(mcpPath);
				await api.mcp.streamableHttpDelete(session, mcpPath);

				// Try to use the deleted session - should fail
				const message = api.mcp.createMessage('tools/list');
				const response = await api.mcp.streamableHttpSendMessage(session, mcpPath, message);

				expect(response.status()).toBeGreaterThanOrEqual(400);
			});
		});

		test.describe('Error Handling', () => {
			test('should return 404 for non-existent endpoint', async ({ api }) => {
				const response = await api.webhooks.trigger('mcp/non-existent-mcp-endpoint-12345', {
					method: 'POST',
					headers: { 'Content-Type': 'application/json' },
					data: api.mcp.createMessage('initialize'),
				});

				expect(response.status()).toBe(404);
			});

			test('should handle malformed JSON-RPC messages', async ({ api }) => {
				const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
					'mcp-trigger/mcp-trigger-basic.json',
				);
				await api.workflows.activate(workflowId, createdWorkflow.versionId!);

				const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
				const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

				// First establish a valid session
				const session = await api.mcp.streamableHttpInitialize(mcpPath);

				// Send malformed message (missing required fields)
				const malformedMessage = {
					// Missing jsonrpc version
					id: nanoid(),
					method: 'tools/list',
				};

				const response = await api.mcp.streamableHttpSendMessage(
					session,
					mcpPath,
					malformedMessage,
				);

				// Server should handle gracefully (either error response or parse error)
				// The exact behavior depends on implementation
				const body = await response.text();
				expect(body).toBeTruthy(); // Should return some response
			});
		});
	},
);

// Queue mode tests - tagged with @mode:queue to run only in queue infrastructure
test.describe('MCP Trigger - Queue Mode', () => {
	test('@mode:queue should return 202 Accepted for tool call in queue mode', async ({ api }) => {
		const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
			'mcp-trigger/mcp-trigger-basic.json',
		);
		await api.workflows.activate(workflowId, createdWorkflow.versionId!);

		const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
		const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

		// In SSE mode with queue mode enabled, tool calls should return 202 Accepted
		// because the execution is queued and response comes via Redis pub/sub
		const session = await api.mcp.sseSetup(mcpPath);

		try {
			const message = api.mcp.createMessage('tools/call', {
				name: 'echo',
				arguments: { message: 'Queue mode test' },
			});

			const response = await api.mcp.sseSendMessage(session, message);

			// In queue mode, SSE tool calls may return 202 Accepted
			// The actual result would come back asynchronously
			expect([200, 202]).toContain(response.status());
		} finally {
			api.mcp.sseClose(session);
		}
	});
});

// Multi-main tests - tagged with @mode:multi-main to run only in multi-main infrastructure
test.describe('MCP Trigger - Multi-Main', () => {
	test.describe('Streamable HTTP Transport', () => {
		test('@mode:multi-main should handle tool call on different main than session creator', async ({
			api,
			mainUrls,
			createApiForMain,
		}) => {
			// This test verifies that MCP sessions work correctly in multi-main setups
			// where the session might be created on one main but tool calls routed to another

			// Skip if not in multi-main mode (need at least 2 mains)
			test.skip(mainUrls.length < 2, 'Requires at least 2 mains for multi-main testing');

			// Create workflow via load balancer (normal flow)
			const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
				'mcp-trigger/mcp-trigger-basic.json',
			);
			await api.workflows.activate(workflowId, createdWorkflow.versionId!);

			const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
			const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

			// Initialize session on main-1 (direct access, bypassing load balancer)
			const main1Api = await createApiForMain(0);
			const session = await main1Api.mcp.streamableHttpInitialize(mcpPath);

			expect(session.sessionId).toBeTruthy();

			// Send tool call to main-2 (different main than where session was created)
			// This tests that session state is properly shared across mains via Redis
			const main2Api = await createApiForMain(1);
			const result = await main2Api.mcp.callTool(session, mcpPath, 'echo', {
				message: 'Cross-main test',
			});

			expect(result.content).toBeDefined();
			expect(result.content[0].text).toContain('Cross-main test');
		});

		test('@mode:multi-main should handle multiple tool calls across different mains', async ({
			api,
			mainUrls,
			createApiForMain,
		}) => {
			// Test that multiple tool calls can be distributed across mains
			test.skip(mainUrls.length < 2, 'Requires at least 2 mains for multi-main testing');

			const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
				'mcp-trigger/mcp-trigger-multi-tool.json',
			);
			await api.workflows.activate(workflowId, createdWorkflow.versionId!);

			const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
			const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

			// Initialize session on main-1
			const main1Api = await createApiForMain(0);
			const session = await main1Api.mcp.streamableHttpInitialize(mcpPath);

			// Alternate tool calls between mains to simulate load balancer behavior
			const main2Api = await createApiForMain(1);

			// Call 1: main-1
			const echoResult = await main1Api.mcp.callTool(session, mcpPath, 'echo', {
				message: 'From main 1',
			});
			expect(echoResult.content[0].text).toContain('From main 1');

			// Call 2: main-2
			const addResult = await main2Api.mcp.callTool(session, mcpPath, 'add', { a: 10, b: 20 });
			expect(addResult.content[0].text).toContain('30');

			// Call 3: main-1 again
			const multiplyResult = await main1Api.mcp.callTool(session, mcpPath, 'multiply', {
				a: 5,
				b: 6,
			});
			expect(multiplyResult.content[0].text).toContain('30');

			// Call 4: main-2 again
			const echoResult2 = await main2Api.mcp.callTool(session, mcpPath, 'echo', {
				message: 'From main 2',
			});
			expect(echoResult2.content[0].text).toContain('From main 2');
		});
	});

	test.describe('SSE Transport', () => {
		test('@mode:multi-main should handle SSE tool call on different main than session creator', async ({
			api,
			mainUrls,
			createApiForMain,
		}) => {
			// This test verifies that SSE-based MCP sessions work correctly in multi-main setups
			test.skip(mainUrls.length < 2, 'Requires at least 2 mains for multi-main testing');

			const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
				'mcp-trigger/mcp-trigger-basic.json',
			);
			await api.workflows.activate(workflowId, createdWorkflow.versionId!);

			const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
			const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

			// Initialize SSE session on main-1
			const main1Api = await createApiForMain(0);
			const session = await main1Api.mcp.sseSetup(mcpPath);

			try {
				expect(session.sessionId).toBeTruthy();
				expect(session.transport).toBe('sse');

				// Send tool call to main-2 (different main than where SSE session was created)
				// This tests that SSE session state is properly shared across mains via Redis
				// Use callToolCrossMain to POST to main2's URL while receiving on main1's SSE stream
				const main2McpPath = `${mainUrls[1]}/${mcpPath}`;
				const result = await main1Api.mcp.callToolCrossMain(session, main2McpPath, 'echo', {
					message: 'SSE cross-main test',
				});

				expect(result.content).toBeDefined();
				expect(result.content[0].text).toContain('SSE cross-main test');
			} finally {
				main1Api.mcp.sseClose(session);
			}
		});

		test('@mode:multi-main should handle multiple SSE tool calls across different mains', async ({
			api,
			mainUrls,
			createApiForMain,
		}) => {
			// Test that multiple SSE tool calls can be distributed across mains
			test.skip(mainUrls.length < 2, 'Requires at least 2 mains for multi-main testing');

			const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
				'mcp-trigger/mcp-trigger-multi-tool.json',
			);
			await api.workflows.activate(workflowId, createdWorkflow.versionId!);

			const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
			const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

			// Initialize SSE session on main-1
			const main1Api = await createApiForMain(0);
			const session = await main1Api.mcp.sseSetup(mcpPath);

			// Construct full URL paths for cross-main calls
			const main2McpPath = `${mainUrls[1]}/${mcpPath}`;

			try {
				// Call 1: main-1 (where SSE connection was established)
				const echoResult = await main1Api.mcp.callTool(session, mcpPath, 'echo', {
					message: 'SSE from main 1',
				});
				expect(echoResult.content[0].text).toContain('SSE from main 1');

				// Call 2: main-2 (different main, tests Redis pub/sub for response routing)
				// Use callToolCrossMain to POST to main2 but receive response on main1's SSE stream
				const addResult = await main1Api.mcp.callToolCrossMain(session, main2McpPath, 'add', {
					a: 15,
					b: 25,
				});
				expect(addResult.content[0].text).toContain('40');

				// Call 3: main-1 again
				const multiplyResult = await main1Api.mcp.callTool(session, mcpPath, 'multiply', {
					a: 7,
					b: 8,
				});
				expect(multiplyResult.content[0].text).toContain('56');

				// Call 4: main-2 again
				const echoResult2 = await main1Api.mcp.callToolCrossMain(session, main2McpPath, 'echo', {
					message: 'SSE from main 2',
				});
				expect(echoResult2.content[0].text).toContain('SSE from main 2');
			} finally {
				main1Api.mcp.sseClose(session);
			}
		});

		test('@mode:multi-main should list tools via SSE from different main', async ({
			api,
			mainUrls,
			createApiForMain,
		}) => {
			// Test that tools/list works across mains with SSE transport
			test.skip(mainUrls.length < 2, 'Requires at least 2 mains for multi-main testing');

			const { workflowId, createdWorkflow } = await api.workflows.importWorkflowFromFile(
				'mcp-trigger/mcp-trigger-multi-tool.json',
			);
			await api.workflows.activate(workflowId, createdWorkflow.versionId!);

			const mcpNode = createdWorkflow.nodes?.find((n) => n.type.includes('mcpTrigger'));
			const mcpPath = `mcp/${mcpNode?.parameters.path as string}`;

			// Initialize SSE session on main-1
			const main1Api = await createApiForMain(0);
			const session = await main1Api.mcp.sseSetup(mcpPath);

			try {
				// List tools via main-2 (different main)
				// Use listToolsCrossMain to POST to main2 but receive response on main1's SSE stream
				const main2McpPath = `${mainUrls[1]}/${mcpPath}`;
				const tools = await main1Api.mcp.listToolsCrossMain(session, main2McpPath);

				expect(tools).toHaveLength(3);
				const toolNames = tools.map((t) => t.name).sort();
				expect(toolNames).toEqual(['add', 'echo', 'multiply']);
			} finally {
				main1Api.mcp.sseClose(session);
			}
		});
	});
});
