Skip to main content
Glama
competitorFinder.ts4.05 kB
import { z } from "zod"; import axios from "axios"; import { toolRegistry, API_CONFIG } from "./config.js"; import { ExaSearchRequest, ExaSearchResponse } from "../types.js"; import { createRequestLogger } from "../utils/logger.js"; // Register the competitor finder tool toolRegistry["competitor_finder"] = { name: "competitor_finder", description: "Find competitors of a company using Exa AI - performs targeted searches to identify businesses that offer similar products or services. Describe what the company does (without mentioning its name) and optionally provide the company's website to exclude it from results.", schema: { query: z.string().describe("Describe what the company/product in a few words (e.g., 'web search API', 'AI image generation', 'cloud storage service'). Keep it simple. Do not include the company name."), excludeDomain: z.string().optional().describe("Optional: The company's website to exclude from results (e.g., 'exa.ai')"), numResults: z.number().optional().describe("Number of competitors to return (default: 10)") }, handler: async ({ query, excludeDomain, numResults }, extra) => { const requestId = `competitor_finder-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`; const logger = createRequestLogger(requestId, 'competitor_finder'); logger.start(query); try { // Create a fresh axios instance for each request const axiosInstance = axios.create({ baseURL: API_CONFIG.BASE_URL, headers: { 'accept': 'application/json', 'content-type': 'application/json', 'x-api-key': process.env.EXA_API_KEY || '' }, timeout: 25000 }); const searchRequest: ExaSearchRequest = { query, type: "auto", numResults: numResults || 10, contents: { text: { maxCharacters: API_CONFIG.DEFAULT_MAX_CHARACTERS }, livecrawl: 'always' } }; // Add exclude domain if provided if (excludeDomain) { searchRequest.excludeDomains = [excludeDomain]; logger.log(`Excluding domain: ${excludeDomain}`); } logger.log(`Finding competitors for: ${query}`); logger.log("Sending competitor finder request to Exa API"); const response = await axiosInstance.post<ExaSearchResponse>( API_CONFIG.ENDPOINTS.SEARCH, searchRequest, { timeout: 25000 } ); logger.log("Received competitor finder response from Exa API"); if (!response.data || !response.data.results) { logger.log("Warning: Empty or invalid response from Exa API for competitor finder"); return { content: [{ type: "text" as const, text: "No competitors found. Please try a different query." }] }; } logger.log(`Found ${response.data.results.length} potential competitors`); const result = { content: [{ type: "text" as const, text: JSON.stringify(response.data, null, 2) }] }; logger.complete(); return result; } catch (error) { logger.error(error); if (axios.isAxiosError(error)) { // Handle Axios errors specifically const statusCode = error.response?.status || 'unknown'; const errorMessage = error.response?.data?.message || error.message; logger.log(`Axios error (${statusCode}): ${errorMessage}`); return { content: [{ type: "text" as const, text: `Competitor finder error (${statusCode}): ${errorMessage}` }], isError: true, }; } // Handle generic errors return { content: [{ type: "text" as const, text: `Competitor finder error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } }, enabled: false // Disabled by default };

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/jackedelic/exa-mcp-server'

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