Skip to main content
Glama
search.js3.57 kB
import { search } from "kagi-ken"; import { formatError, formatSearchResults, getEnvironmentConfig, } from "../utils/formatting.js"; import { z } from "zod"; /** * Schema for search tool input validation */ export const searchInputSchema = { queries: z.array(z.string()).min(1).describe( "One or more concise, keyword-focused search queries. Include essential context within each query for standalone use.", ), limit: z.number().int().min(1).max(50).optional().describe( "Maximum number of search results per query (default: 10, max: 50)", ), }; /** * Kagi search tool implementation using kagi-ken package * Mirrors the functionality of the official Kagi MCP kagi_search_fetch tool * * @param {Object} args - Tool arguments * @param {Array<string>} args.queries - Array of search queries * @param {number} [args.limit=10] - Maximum number of search results per query * @returns {Promise<Object>} MCP tool response */ export async function kagiSearchFetch({ queries, limit = 10 }) { try { if (!queries || queries.length === 0) { throw new Error("Search called with no queries."); } const { token } = getEnvironmentConfig(); // Execute searches concurrently (similar to ThreadPoolExecutor in original) const searchPromises = queries.map((query) => { if (typeof query !== "string" || query.trim() === "") { throw new Error("All queries must be non-empty strings"); } return search(query.trim(), token, limit); }); // Wait for all searches to complete with 10 second timeout per search const results = await Promise.allSettled( searchPromises.map((promise) => Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error("Search timeout")), 10000) ), ]) ), ); // Process results and handle any failures const responses = []; const errors = []; for (let i = 0; i < results.length; i++) { const result = results[i]; if (result.status === "fulfilled") { responses.push(result.value); } else { errors.push( `Query "${queries[i]}": ${result.reason?.message || result.reason}`, ); // Add empty response to maintain index alignment responses.push({ results: [] }); } } // Format results using the same formatting as official MCP const formattedResults = formatSearchResults(queries, responses); // Include any errors in the response let finalResponse = formattedResults; if (errors.length > 0) { finalResponse += "\n\nErrors encountered:\n" + errors.join("\n"); } return { content: [ { type: "text", text: finalResponse, }, ], }; } catch (error) { return { content: [ { type: "text", text: formatError(error), }, ], }; } } /** * Tool registration configuration for MCP server */ export const searchToolConfig = { name: "kagi_search_fetch", description: ` Fetch web results based on one or more queries using the Kagi.com web search engine. Use for general search and when the user explicitly tells you to 'fetch' results/information. Results are from all queries given. They are numbered continuously, so that a user may be able to refer to a result by a specific number. Supports optional limit parameter to control results per query. `.replace(/\s+/gs, " ").trim(), inputSchema: searchInputSchema, };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/czottmann/kagi-ken-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server