Skip to main content
Glama
yuki-yano

Kagi MCP

by yuki-yano

kagi_search_fetch

Execute web searches using Kagi's API to retrieve and organize results for one or more queries. Enables users to reference specific results by number for efficient information retrieval.

Instructions

Fetch web results based on one or more queries using the Kagi Search API. 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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queriesYesOne or more concise, keyword-focused search queries. Include essential context within each query for standalone use.

Implementation Reference

  • Main handler logic for 'kagi_search_fetch' tool: validates input queries, executes parallel searches using KagiClient, formats results with formatSearchResults, and returns formatted text content or error.
    if (name === 'kagi_search_fetch') {
      const queries = args?.queries as string[] | undefined;
      
      if (!queries || queries.length === 0) {
        throw new McpError(
          ErrorCode.InvalidParams,
          'Search called with no queries.'
        );
      }
      
      try {
        // Execute searches in parallel
        const searchPromises = queries.map(query => kagiClient.search(query));
        const results = await Promise.all(searchPromises);
        
        const formattedResults = formatSearchResults(queries, results);
        
        return {
          content: [
            {
              type: 'text',
              text: formattedResults,
            },
          ],
        };
      } catch (error) {
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${error instanceof Error ? error.message : String(error)}`,
            },
          ],
        };
      }
    }
  • Input schema and metadata definition for the 'kagi_search_fetch' tool, including name, description, and JSON schema for queries array.
    const SEARCH_TOOL: Tool = {
      name: 'kagi_search_fetch',
      description: 'Fetch web results based on one or more queries using the Kagi Search API. 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.',
      inputSchema: {
        type: 'object',
        properties: {
          queries: {
            type: 'array',
            items: {
              type: 'string',
            },
            description: 'One or more concise, keyword-focused search queries. Include essential context within each query for standalone use.',
          },
        },
        required: ['queries'],
      },
    };
  • src/index.ts:69-73 (registration)
    Registration of the 'kagi_search_fetch' tool (as SEARCH_TOOL) in the MCP server's listTools handler.
    server.setRequestHandler(ListToolsRequestSchema, async (): Promise<ListToolsResult> => {
      return {
        tools: [SEARCH_TOOL, SUMMARIZER_TOOL],
      };
    });
  • Helper function to format search results from multiple queries into a continuous numbered list grouped by query.
    function formatSearchResults(queries: string[], responses: SearchResponse[]): string {
      const resultTemplate = (result: {
        result_number: number;
        title: string;
        url: string;
        published: string;
        snippet: string;
      }) => `${result.result_number}: ${result.title}
    ${result.url}
    Published Date: ${result.published}
    ${result.snippet}`;
    
      const queryResponseTemplate = (query: string, formattedResults: string) => `-----
    Results for search query "${query}":
    -----
    ${formattedResults}`;
    
      const perQueryResponseStrs: string[] = [];
      let startIndex = 1;
    
      for (let i = 0; i < queries.length; i++) {
        const query = queries[i];
        const response = responses[i];
        
        // t == 0 is search result, t == 1 is related searches
        const results = response.data.filter(result => result.t === 0);
        
        const formattedResultsList = results.map((result, idx) => 
          resultTemplate({
            result_number: startIndex + idx,
            title: result.title,
            url: result.url,
            published: result.published || 'Not Available',
            snippet: result.snippet,
          })
        );
        
        startIndex += results.length;
        
        const formattedResultsStr = formattedResultsList.join('\n\n');
        const queryResponseStr = queryResponseTemplate(query, formattedResultsStr);
        perQueryResponseStrs.push(queryResponseStr);
      }
      
      return perQueryResponseStrs.join('\n\n');
    }
  • KagiClient.search method: performs HTTP GET to Kagi search API with query, handles errors, returns SearchResponse.
    async search(query: string): Promise<SearchResponse> {
      try {
        const response = await this.axios.get('/search', {
          params: { q: query },
        });
        return response.data;
      } catch (error) {
        if (axios.isAxiosError(error)) {
          let errorMessage = 'Unknown error';
          if (error.response?.data) {
            if (typeof error.response.data === 'string') {
              errorMessage = error.response.data;
            } else if (error.response.data.error) {
              // Kagi API returns error as an array
              if (Array.isArray(error.response.data.error)) {
                errorMessage = error.response.data.error.map((e: any) => e.msg).join('; ');
              } else {
                errorMessage = error.response.data.error;
              }
            } else if (error.response.data.message) {
              errorMessage = error.response.data.message;
            } else {
              errorMessage = JSON.stringify(error.response.data);
            }
          } else {
            errorMessage = error.response?.statusText || error.message;
          }
          
          const statusCode = error.response?.status;
          throw new Error(`Kagi search error (${statusCode}): ${errorMessage}`);
        }
        throw error;
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It explains that 'Results are from all queries given' and 'They are numbered continuously, so that a user may be able to refer to a result by a specific number,' which adds useful context about result aggregation and numbering. However, it doesn't cover important behavioral aspects like rate limits, authentication needs, pagination, or error handling, leaving significant gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise with three sentences that each serve a distinct purpose: stating the tool's function, providing usage guidelines, and explaining result formatting. It's front-loaded with the core purpose. A slight improvement could be made by combining the last two sentences for even tighter structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (search API with result aggregation), no annotations, and no output schema, the description provides adequate but incomplete context. It covers the basic purpose, usage triggers, and result numbering, but lacks details about return format, error cases, rate limits, or how results from multiple queries are integrated. This is minimally viable but has clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, with the 'queries' parameter well-documented in the schema itself. The description doesn't add any meaningful parameter semantics beyond what's already in the schema description ('One or more concise, keyword-focused search queries'). This meets the baseline of 3 when schema coverage is high.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Fetch web results based on one or more queries using the Kagi Search API.' It specifies the verb ('fetch'), resource ('web results'), and method ('Kagi Search API'). However, it doesn't explicitly differentiate from its sibling tool 'kagi_summarizer' beyond the general search focus, which prevents a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear usage context: 'Use for general search and when the user explicitly tells you to 'fetch' results/information.' This gives practical guidance on when to invoke the tool. It doesn't explicitly state when NOT to use it or mention alternatives like the sibling summarizer tool, which keeps it from a score of 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/yuki-yano/kagi-mcp'

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