| 35 | * Create the fetch tool entry for the agent tool registry. |
| 36 | */ |
| 37 | export function createFetchToolEntry( |
| 38 | opts: FetchToolOptions = {}, |
| 39 | ): Record<string, ActionEntry> { |
| 40 | return { |
| 41 | "web-request": { |
| 42 | tool: { |
| 43 | description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered action tools directly (e.g. \`log-meal\`, \`bigquery\`, \`hubspot-deals\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`, |
| 44 | parameters: { |
| 45 | type: "object" as const, |
| 46 | properties: { |
| 47 | url: { |
| 48 | type: "string", |
| 49 | description: |
| 50 | 'Full URL. May contain ${keys.NAME} references, e.g. "${keys.SLACK_WEBHOOK}".', |
| 51 | }, |
| 52 | method: { |
| 53 | type: "string", |
| 54 | description: "HTTP method. Default: GET.", |
| 55 | enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"], |
| 56 | }, |
| 57 | headers: { |
| 58 | type: "string", |
| 59 | description: |
| 60 | 'JSON object of headers. May contain ${keys.NAME} references. Example: \'{"Authorization": "Bearer ${keys.API_TOKEN}"}\'.', |
| 61 | }, |
| 62 | body: { |
| 63 | type: "string", |
| 64 | description: |
| 65 | "Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.", |
| 66 | }, |
| 67 | timeout_ms: { |
| 68 | type: "number", |
| 69 | description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`, |
| 70 | }, |
| 71 | }, |
| 72 | required: ["url"], |
| 73 | }, |
| 74 | }, |
| 75 | run: async (args: Record<string, string>) => { |
| 76 | const startTime = Date.now(); |
| 77 | const rawUrl = args.url; |
| 78 | const method = normalizeToolProxyMethod(args.method || "GET"); |
| 79 | if (!method) { |
| 80 | return "Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD."; |
| 81 | } |
| 82 | const rawHeaders = args.headers || "{}"; |
| 83 | const rawBody = args.body; |
| 84 | const timeoutMs = Math.min( |
| 85 | Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS, |
| 86 | 30_000, |
| 87 | ); |
| 88 | |
| 89 | // Resolve key references |
| 90 | let resolvedUrl = rawUrl; |
| 91 | let resolvedHeaders = rawHeaders; |
| 92 | let resolvedBody = rawBody; |
| 93 | const allUsedKeys: string[] = []; |
| 94 | const allSecretValues: string[] = []; |