src / toolsProvider.ts
import { text, tool, type Tool, type ToolsProviderController } from "@lmstudio/sdk";
import { z } from "zod";
import { configSchematics } from "./configSchematics";
export async function toolsProvider(ctl: ToolsProviderController) {
const tools: Tool[] = [];
// Retrieve plugin configuration
const pluginConfig = ctl.getPluginConfig(configSchematics);
const braveApiKey = pluginConfig.get("braveApiKey");
// Fetch URL Tool (general HTTP GET, no auth needed)
const fetchTool = tool({
name: "fetch_url",
description: text`
Perform an HTTP GET request to a URL and return the response content as text.
Useful for accessing web pages, APIs, or files. Handles text-based responses (e.g., HTML, JSON).
If the content is too large, it may be truncated; check status for warnings.
`,
parameters: {
url: z.string().url().describe("The URL to fetch (must be a valid HTTP/HTTPS URL)."),
},
implementation: async ({ url }, { status, warn, signal }) => {
try {
status(`Fetching URL: ${url}`);
const response = await fetch(url, { signal });
if (!response.ok) {
return `Error: ${response.status} ${response.statusText}`;
}
const text = await response.text();
if (text.length > 100000) { // Arbitrary limit to prevent overwhelming the model
warn("Response content is very large; truncating to first 100,000 characters.");
return text.slice(0, 100000) + "\n\n[Truncated: Content too long]";
}
return text;
} catch (error) {
return `Error fetching URL: ${error.message}`;
}
},
});
tools.push(fetchTool);
// Search Web Tool (using Brave Search API)
const searchTool = tool({
name: "search_web",
description: text`
Search the web using the Brave Search API and return formatted results (titles, URLs, descriptions).
Returns up to 10 results. Use for general queries; for specific pages, use fetch_url.
`,
parameters: {
query: z.string().describe("The search query (e.g., 'latest AI news 2025')."),
count: z.number().int().min(1).max(20).optional().default(10).describe("Number of results (default 10, max 20)."),
country: z.string().optional().default("us").describe("Country code for localized results (e.g., 'us', 'gb')."),
lang: z.string().optional().default("en").describe("Search language (e.g., 'en', 'fr')."),
},
implementation: async ({ query, count, country, lang }, { status, warn, signal }) => {
try {
status(`Searching web for: ${query}`);
const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${count}&country=${country}&search_lang=${lang}`;
const response = await fetch(url, {
headers: { "X-Subscription-Token": braveApiKey },
signal,
});
if (!response.ok) {
if (response.status === 429) {
warn("Rate limit exceeded; check your Brave API quota.");
}
return `Error: ${response.status} ${response.statusText}`;
}
const data = await response.json();
if (!data.web || !data.web.results) {
return "No results found.";
}
// Format results for the model
let formatted = "Search Results:\n";
data.web.results.slice(0, count).forEach((result: any, index: number) => {
formatted += `${index + 1}. Title: ${result.title}\n URL: ${result.url}\n Description: ${result.description}\n\n`;
});
return formatted;
} catch (error) {
return `Error during search: ${error.message}`;
}
},
});
tools.push(searchTool);
return tools;
}