1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5import { Type } from "@sinclair/typebox";
6import type { AgentTool } from "@mariozechner/pi-agent-core";
7import Tabstack from "@tabstack/sdk";
8import { FetchError, ToolInputError } from "../../util/errors.js";
9
10export interface WebFetchResult {
11 content: string;
12 url: string;
13}
14
15const FetchSchema = Type.Object({
16 url: Type.String({ description: "URL to fetch" }),
17 nocache: Type.Optional(Type.Boolean({ description: "Bypass cache and fetch fresh content" })),
18});
19
20export function createWebFetchTool(apiKey: string): AgentTool {
21 return {
22 name: "web_fetch",
23 label: "Web Fetch",
24 description: "Fetch a URL and return its content as markdown.",
25 parameters: FetchSchema as any,
26 execute: async (_toolCallId: string, params: any) => {
27 if (!apiKey) {
28 throw new ToolInputError("Web fetch is not configured");
29 }
30
31 const client = new Tabstack({ apiKey });
32
33 try {
34 const result = await client.extract.markdown({
35 url: params.url,
36 nocache: params.nocache ?? false,
37 });
38
39 return {
40 content: [{ type: "text", text: result.content ?? "" }],
41 details: { url: params.url, length: result.content?.length ?? 0 },
42 };
43 } catch (error: any) {
44 throw new FetchError(params.url, error?.message ?? String(error));
45 }
46 },
47 };
48}