Files
Keep/keep-notes/lib/ai/tools/url-fetch.tool.ts

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}` }
}
},
}),
})