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 { search } from "kagi-ken";
8import { FetchError, ToolInputError } from "../../util/errors.js";
9
10const SearchSchema = Type.Object({
11 query: Type.String({ description: "Search query" }),
12});
13
14export const createWebSearchTool = (sessionToken: string): AgentTool => ({
15 name: "web_search",
16 label: "Web Search",
17 description: "Search the web. Returns structured results with titles, URLs, and snippets.",
18 parameters: SearchSchema as any,
19 execute: async (_toolCallId: string, params: any) => {
20 if (!sessionToken) {
21 throw new ToolInputError("Web search is not configured");
22 }
23
24 try {
25 const result = await search(params.query, sessionToken);
26 return {
27 content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
28 details: { query: params.query, resultCount: result?.data?.length ?? 0 },
29 };
30 } catch (error: any) {
31 throw new FetchError(
32 `kagi:search?q=${encodeURIComponent(params.query)}`,
33 error?.message ?? String(error),
34 );
35 }
36 },
37});