56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
/**
|
|
* 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}` }
|
|
}
|
|
},
|
|
}),
|
|
})
|