/** * URL Fetch Tool * Fetches a URL and returns parsed content (JSON, CSV, or text). * Max 5MB response. */ import { tool } from 'ai' import { z } from 'zod' import { toolRegistry } from './registry' const MAX_SIZE = 5 * 1024 * 1024 // 5MB toolRegistry.register({ name: 'url_fetch', description: 'Fetch a URL and return its content. Supports JSON, CSV, and plain text responses. Max 5MB.', isInternal: true, buildTool: (_ctx) => tool({ description: 'Fetch a URL and return its parsed content. Supports JSON, CSV, and text.', inputSchema: z.object({ url: z.string().describe('The URL to fetch'), method: z.enum(['GET', 'POST']).optional().describe('HTTP method (default GET)').default('GET'), }), execute: async ({ url, method = 'GET' }) => { try { const response = await fetch(url, { method }) if (!response.ok) return { error: `HTTP ${response.status}: ${response.statusText}` } const contentLength = parseInt(response.headers.get('content-length') || '0') if (contentLength > MAX_SIZE) return { error: 'Response too large (max 5MB)' } const contentType = response.headers.get('content-type') || '' const text = await response.text() if (text.length > MAX_SIZE) return { error: 'Response too large (max 5MB)' } if (contentType.includes('application/json')) { try { return { type: 'json', data: JSON.parse(text) } } catch { return { type: 'text', content: text.substring(0, 10000) } } } if (contentType.includes('text/csv')) { return { type: 'csv', content: text.substring(0, 10000) } } return { type: 'text', content: text.substring(0, 10000) } } catch (e: any) { return { error: `Fetch failed: ${e.message}` } } }, }), })