#!/usr/bin/env node /** * Batch fix for tier-2 tool modules * Converts from server.tool() API to registry.registerTool() API */ const fs = require('fs'); const path = require('path'); const files = [ 'src/tools/audiences.ts', 'src/tools/budget.ts', 'src/tools/catalog.ts', 'src/tools/competitive.ts', 'src/tools/experiments.ts', 'src/tools/leads.ts', ]; function convertFile(filePath) { console.log(`Converting ${filePath}...`); let content = fs.readFileSync(filePath, 'utf8'); // 1. Replace imports content = content.replace( /import type { McpServer } from "@modelcontextprotocol\/sdk\/server\/mcp\.js";/g, '' ); content = content.replace( /interface MetaApiClient \{[^}]+\}/gs, '' ); // Add ToolRegistry import if not present if (!content.includes('import type { ToolRegistry }')) { content = content.replace( /(import { z } from "zod";)/, '$1\nimport type { ToolRegistry } from "../server.js";' ); } // Remove ToolAnnotations from imports if present content = content.replace( /,\s*ToolAnnotations/g, '' ); // 2. Replace function signature content = content.replace( /export function register\(server: McpServer, client: MetaApiClient\): void \{/g, (match, offset) => { // Get the function name from the file const baseName = path.basename(filePath, '.ts'); const functionName = 'register' + baseName.charAt(0).toUpperCase() + baseName.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + 'Tools'; return `export function ${functionName}(registry: ToolRegistry): void {\n const client = registry.getClient();`; } ); // 3. Convert server.tool() calls to registry.registerTool() // This regex handles the basic structure content = content.replace( /const\s+(\w+Annotations):\s*ToolAnnotations\s*=\s*(\{[^}]+\});[\s\n]+server\.tool\(\s*"([^"]+)",\s*"([^"]+)",\s*(\{[^}]+\}),\s*async\s*\(params\)\s*=>\s*\{/gs, (match, annotationsVar, annotations, toolName, description, schema) => { return `registry.registerTool({\n name: "${toolName}",\n description: "${description}",\n inputSchema: z.object(${schema}),\n annotations: ${annotations},\n handler: async (args) => {\n const params = args as any;`; } ); // 4. Fix tool closing - replace "),\n annotationsVar\n );" with "},\n });" content = content.replace( /\},\s*\w+Annotations\s*\);/g, '};\n\n return {\n content: [{\n type: \'text\',\n text: JSON.stringify(result, null, 2),\n }],\n };\n },\n });' ); // Fix client.get calls to add type cast content = content.replace( /client\.get<([^>]+)>\(([^,]+),\s*([^)]+)\)/g, 'client.get<$1>($2, $3 as Record)' ); fs.writeFileSync(filePath, content, 'utf8'); console.log(` ✓ Converted ${filePath}`); } // Convert all files for (const file of files) { const filePath = path.join(__dirname, file); if (fs.existsSync(filePath)) { try { convertFile(filePath); } catch (error) { console.error(` ✗ Error converting ${file}:`, error.message); } } else { console.error(` ✗ File not found: ${filePath}`); } } console.log('\nDone! Please review the changes and run npx tsc --noEmit to check.');