Skip to main content
Glama

MCP Server for Crawl4AI

by omgwtfwow
index.cli.test.ts5.85 kB
// import { jest } from '@jest/globals'; import { spawn } from 'child_process'; import * as path from 'path'; import * as url from 'url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); describe('CLI Entry Point', () => { const cliPath = path.join(__dirname, '..', '..', 'src', 'index.ts'); // Helper to run CLI with given env vars const runCLI = ( env: Record<string, string> = {}, ): Promise<{ code: number | null; stdout: string; stderr: string }> => { return new Promise((resolve) => { const child = spawn('tsx', [cliPath], { env: { ...process.env, ...env }, stdio: 'pipe', }); let stdout = ''; let stderr = ''; child.stdout.on('data', (data) => { stdout += data.toString(); }); child.stderr.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { resolve({ code, stdout, stderr }); }); // Kill after 2 seconds to prevent hanging setTimeout(() => { child.kill(); }, 2000); }); }; describe('Environment Variable Validation', () => { it('should exit with code 1 when CRAWL4AI_BASE_URL is missing', async () => { const { code, stderr } = await runCLI({ CRAWL4AI_BASE_URL: '', }); expect(code).toBe(1); expect(stderr).toContain('Error: CRAWL4AI_BASE_URL environment variable is required'); expect(stderr).toContain('Please set it to your Crawl4AI server URL'); }); it('should start successfully with valid CRAWL4AI_BASE_URL', async () => { const { code, stderr } = await runCLI({ CRAWL4AI_BASE_URL: 'http://localhost:11235', CRAWL4AI_API_KEY: 'test-key', }); // Process should be killed by timeout, not exit with error expect(code).not.toBe(1); // MCP servers output to stderr expect(stderr).toContain('crawl4ai-mcp'); }); it('should use default values for optional env vars', async () => { const { stderr } = await runCLI({ CRAWL4AI_BASE_URL: 'http://localhost:11235', // No API_KEY, SERVER_NAME, or SERVER_VERSION }); expect(stderr).toContain('crawl4ai-mcp'); // default server name expect(stderr).toContain('1.0.0'); // default version }); it('should use custom SERVER_NAME and SERVER_VERSION when provided', async () => { const { stderr } = await runCLI({ CRAWL4AI_BASE_URL: 'http://localhost:11235', SERVER_NAME: 'custom-server', SERVER_VERSION: '2.0.0', }); expect(stderr).toContain('custom-server'); expect(stderr).toContain('2.0.0'); }); }); describe('Signal Handling', () => { it('should handle SIGTERM gracefully', async () => { const child = spawn('tsx', [cliPath], { env: { ...process.env, CRAWL4AI_BASE_URL: 'http://localhost:11235', }, stdio: 'pipe', }); // Wait for startup await new Promise((resolve) => setTimeout(resolve, 500)); // Send SIGTERM child.kill('SIGTERM'); const code = await new Promise<number | null>((resolve, reject) => { const timeout = setTimeout(() => { child.kill('SIGKILL'); reject(new Error('Process did not exit in time')); }, 5000); child.on('close', (exitCode) => { clearTimeout(timeout); resolve(exitCode); }); }); // Should exit with signal code expect(code).toBe(143); // 128 + 15 (SIGTERM) // Ensure cleanup child.kill(); }, 10000); it('should handle SIGINT gracefully', async () => { const child = spawn('tsx', [cliPath], { env: { ...process.env, CRAWL4AI_BASE_URL: 'http://localhost:11235', }, stdio: 'pipe', }); // Wait for startup await new Promise((resolve) => setTimeout(resolve, 500)); // Send SIGINT (Ctrl+C) child.kill('SIGINT'); const code = await new Promise<number | null>((resolve, reject) => { const timeout = setTimeout(() => { child.kill('SIGKILL'); reject(new Error('Process did not exit in time')); }, 5000); child.on('close', (exitCode) => { clearTimeout(timeout); resolve(exitCode); }); }); // Should exit with signal code expect(code).toBe(130); // 128 + 2 (SIGINT) // Ensure cleanup child.kill(); }, 10000); }); describe('Error Handling', () => { it('should handle server startup errors', async () => { // This will be tricky to test without mocking, but we can at least // verify the process starts and attempts to connect const { code, stdout, stderr } = await runCLI({ CRAWL4AI_BASE_URL: 'http://invalid-host-that-does-not-exist:99999', }); // Should not exit with code 1 (that's for missing env vars) expect(code).not.toBe(1); // But might log connection errors const output = stdout + stderr; expect(output).toBeTruthy(); }); }); describe('dotenv Loading', () => { it('should load .env file if present', async () => { // Create a temporary .env file const fs = await import('fs/promises'); const envPath = path.join(__dirname, '..', '..', '.env.test'); await fs.writeFile(envPath, 'TEST_ENV_VAR=loaded_from_file\n'); try { const { stderr } = await runCLI({ CRAWL4AI_BASE_URL: 'http://localhost:11235', NODE_ENV: 'test', DOTENV_CONFIG_PATH: envPath, }); // Verify the server starts (dotenv loaded successfully) expect(stderr).toContain('crawl4ai-mcp'); } finally { // Clean up await fs.unlink(envPath).catch(() => {}); } }); }); });

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/omgwtfwow/mcp-crawl4ai-ts'

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