Skip to main content
Glama
add-timeout-to-tools.js6 kB
#!/usr/bin/env node /** * Script to add timeout parameter to all tools * This script will automatically add timeout functionality to all tools in the project */ const fs = require('fs-extra'); const path = require('path'); const { execSync } = require('child_process'); const TOOLS_DIR = path.join(__dirname, 'src', 'tools'); const UTILS_DIR = path.join(__dirname, 'src', 'utils'); async function addTimeoutToAllTools() { console.log('🔧 Adding timeout parameter to all tools...'); // Ensure timeout utility exists const timeoutUtilPath = path.join(UTILS_DIR, 'timeout.ts'); if (!(await fs.pathExists(timeoutUtilPath))) { console.log('❌ Timeout utility not found, creating it...'); await fs.ensureDir(UTILS_DIR); const timeoutUtilContent = `/** * Timeout utility for operations * Provides timeout functionality with AbortController */ export interface TimeoutOptions { timeout?: number; // timeout in milliseconds signal?: AbortSignal; } /** * Wraps a promise with timeout functionality * @param promise The promise to wrap * @param timeoutMs Timeout in milliseconds * @param timeoutMessage Custom timeout message * @returns Promise that rejects with timeout error if timeout is exceeded */ export function withTimeout<T>( promise: Promise<T>, timeoutMs: number, timeoutMessage: string = \`Operation timed out after \${timeoutMs}ms\` ): Promise<T> { return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error(timeoutMessage)); }, timeoutMs); promise .then((result) => { clearTimeout(timeoutId); resolve(result); }) .catch((error) => { clearTimeout(timeoutId); reject(error); }); }); } /** * Creates an AbortController with timeout * @param timeoutMs Timeout in milliseconds * @returns AbortController that will abort after timeout */ export function createTimeoutController(timeoutMs: number): AbortController { const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); }, timeoutMs); // Clear timeout if operation completes const originalSignal = controller.signal; const cleanup = () => clearTimeout(timeoutId); originalSignal.addEventListener('abort', cleanup); return controller; } /** * Validates and normalizes timeout value * @param timeout Timeout value from parameters * @param defaultTimeout Default timeout in seconds * @param maxTimeout Maximum allowed timeout in seconds * @returns Timeout in milliseconds */ export function normalizeTimeout( timeout?: number, defaultTimeout: number = 120, maxTimeout: number = 600 ): number { const timeoutSeconds = timeout ?? defaultTimeout; const clampedTimeout = Math.min(Math.max(timeoutSeconds, 1), maxTimeout); return clampedTimeout * 1000; // Convert to milliseconds } `; await fs.writeFile(timeoutUtilPath, timeoutUtilContent); console.log('✅ Created timeout utility'); } // Get all tool files const toolFiles = await fs.readdir(TOOLS_DIR); const tsFiles = toolFiles.filter(file => file.endsWith('.ts') && file !== 'index.ts'); console.log(\`📋 Found \${tsFiles.length} tool files\`); for (const file of tsFiles) { const filePath = path.join(TOOLS_DIR, file); const content = await fs.readFile(filePath, 'utf-8'); // Skip if already has timeout import if (content.includes('normalizeTimeout')) { console.log(\`⏭️ Skipping \${file} (already has timeout)\`); continue; } try { // Add timeout import let updatedContent = content.replace( /import.*from.*utils.*timeout.*;/, '' ); // Add timeout import after other imports updatedContent = updatedContent.replace( /(import.*;\s*)$/m, '$1import { withTimeout, normalizeTimeout } from \'../utils/timeout\';\n' ); // Add timeout parameter to inputSchema updatedContent = updatedContent.replace( /(timeout:\s*z\.number\(\)\.optional\(\)\.describe\([^)]+\))/, '$1, // Already exists' ); // Add timeout to inputSchema if not exists if (!updatedContent.includes('timeout:')) { updatedContent = updatedContent.replace( /(inputSchema:\s*\{[^}]*\})/, (match) => { const schema = match.slice(0, -1); // Remove closing brace return schema + ',\n timeout: z.number().optional().describe("Timeout in seconds for the operation (default: 120 seconds, max: 600 seconds)"),\n }'; } ); } // Add timeout to function parameters updatedContent = updatedContent.replace( /(\}\s*=>\s*\{)/, (match) => { if (!match.includes('timeout')) { return match.replace('})', ', timeout })'); } return match; } ); // Add timeout logic to function body updatedContent = updatedContent.replace( /(async\s*\([^)]*\{)/, (match) => { if (!match.includes('timeoutMs')) { return match.replace('{', '{\n // Normalize and apply timeout\n const timeoutMs = normalizeTimeout(timeout);\n\n'); } return match; } ); // Wrap main operation with timeout updatedContent = updatedContent.replace( /(return\s*\{[^}]*content:\s*\[)/, (match) => { return ` // Execute with timeout\n const result = await withTimeout(operation(), timeoutMs, \`Operation timed out after \${timeoutMs}ms\`);\n\n return \{${match.slice(7)}`; } ); await fs.writeFile(filePath, updatedContent); console.log(\`✅ Updated \${file}\`); } catch (error) { console.log(\`❌ Failed to update \${file}: \${error.message}\`); } } console.log('🎉 Finished adding timeout to all tools!'); } // Run the script addTimeoutToAllTools().catch(console.error);

Latest Blog Posts

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/Yussefgafer/MyMCP'

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