Skip to main content
Glama

web_search_exa

Perform real-time web searches and extract content from specific URLs using AI. Configure result counts to retrieve the most relevant information for your queries.

Instructions

Search the web using Exa AI - performs real-time web searches and can scrape content from specific URLs. Supports configurable result counts and returns the content from the most relevant websites.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
numResultsNoNumber of search results to return (default: 5)
queryYesSearch query

Implementation Reference

  • src/index.ts:61-162 (registration)
    Registration of the Exa web search tool named 'search' using McpServer.tool method, including description, Zod input schema, and handler reference.
    this.server.tool( "search", "Search the web using Exa AI - performs real-time web searches and can scrape content from specific URLs. Supports configurable result counts, live crawling options, and returns the content from the most relevant websites.", { query: z.string().describe("Search query"), numResults: z.number().optional().describe("Number of search results to return (default: 5)"), livecrawl: z.enum(['always', 'fallback']).optional().describe("Livecrawl strategy: 'always' to always crawl live, 'fallback' to only crawl when index has no result") }, async ({ query, numResults, livecrawl }) => { const requestId = `search-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`; this.activeRequests.add(requestId); log(`[${requestId}] Starting search for query: "${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': API_KEY }, timeout: 25000 }); const searchRequest: ExaSearchRequest = { query, type: "auto", numResults: numResults || API_CONFIG.DEFAULT_NUM_RESULTS, contents: { text: { maxCharacters: API_CONFIG.DEFAULT_MAX_CHARACTERS }, ...(livecrawl ? { livecrawl } : { livecrawl: 'always' }) } }; log(`[${requestId}] Sending request to Exa API`); const response = await axiosInstance.post<ExaSearchResponse>( API_CONFIG.ENDPOINTS.SEARCH, searchRequest, { timeout: 25000 } ); log(`[${requestId}] Received response from Exa API`); if (!response.data || !response.data.results) { log(`[${requestId}] Warning: Empty or invalid response from Exa API`); return { content: [{ type: "text" as const, text: "No search results found. Please try a different query." }] }; } log(`[${requestId}] Found ${response.data.results.length} results`); const result = { content: [{ type: "text" as const, text: JSON.stringify(response.data, null, 2) }] }; log(`[${requestId}] Successfully completed search`); return result; } catch (error) { log(`[${requestId}] Error processing search: ${error instanceof Error ? error.message : String(error)}`); if (axios.isAxiosError(error)) { // Handle Axios errors specifically const statusCode = error.response?.status || 'unknown'; const errorMessage = error.response?.data?.message || error.message; log(`[${requestId}] Axios error (${statusCode}): ${errorMessage}`); return { content: [{ type: "text" as const, text: `Search error (${statusCode}): ${errorMessage}` }], isError: true, }; } // Handle generic errors return { content: [{ type: "text" as const, text: `Search error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } finally { // Always clean up this.activeRequests.delete(requestId); log(`[${requestId}] Request finalized, ${this.activeRequests.size} active requests remaining`); } } );
  • The core handler function implementing the web search logic using Exa AI API: constructs request, makes authenticated POST to https://api.exa.ai/search, processes results into JSON response, handles errors with detailed logging.
    async ({ query, numResults, livecrawl }) => { const requestId = `search-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`; this.activeRequests.add(requestId); log(`[${requestId}] Starting search for query: "${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': API_KEY }, timeout: 25000 }); const searchRequest: ExaSearchRequest = { query, type: "auto", numResults: numResults || API_CONFIG.DEFAULT_NUM_RESULTS, contents: { text: { maxCharacters: API_CONFIG.DEFAULT_MAX_CHARACTERS }, ...(livecrawl ? { livecrawl } : { livecrawl: 'always' }) } }; log(`[${requestId}] Sending request to Exa API`); const response = await axiosInstance.post<ExaSearchResponse>( API_CONFIG.ENDPOINTS.SEARCH, searchRequest, { timeout: 25000 } ); log(`[${requestId}] Received response from Exa API`); if (!response.data || !response.data.results) { log(`[${requestId}] Warning: Empty or invalid response from Exa API`); return { content: [{ type: "text" as const, text: "No search results found. Please try a different query." }] }; } log(`[${requestId}] Found ${response.data.results.length} results`); const result = { content: [{ type: "text" as const, text: JSON.stringify(response.data, null, 2) }] }; log(`[${requestId}] Successfully completed search`); return result; } catch (error) { log(`[${requestId}] Error processing search: ${error instanceof Error ? error.message : String(error)}`); if (axios.isAxiosError(error)) { // Handle Axios errors specifically const statusCode = error.response?.status || 'unknown'; const errorMessage = error.response?.data?.message || error.message; log(`[${requestId}] Axios error (${statusCode}): ${errorMessage}`); return { content: [{ type: "text" as const, text: `Search error (${statusCode}): ${errorMessage}` }], isError: true, }; } // Handle generic errors return { content: [{ type: "text" as const, text: `Search error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } finally { // Always clean up this.activeRequests.delete(requestId); log(`[${requestId}] Request finalized, ${this.activeRequests.size} active requests remaining`); } }
  • TypeScript interfaces defining the input schema (ExaSearchRequest, SearchArgs) and output schema (ExaSearchResponse) for the Exa web search tool, used for type safety and matching Zod schema.
    export interface ExaSearchRequest { query: string; type: string; numResults: number; contents: { text: { maxCharacters?: number; } | boolean; livecrawl?: 'always' | 'fallback'; }; } export interface ExaSearchResult { id: string; title: string; url: string; publishedDate: string; author: string; text: string; image?: string; favicon?: string; score?: number; } export interface ExaSearchResponse { requestId: string; autopromptString: string; resolvedSearchType: string; results: ExaSearchResult[]; } // Tool Types export interface SearchArgs { query: string; numResults?: number; livecrawl?: 'always' | 'fallback'; }
  • Zod runtime schema for tool input validation parameters: query (required string), numResults (optional number), livecrawl (optional enum).
    query: z.string().describe("Search query"), numResults: z.number().optional().describe("Number of search results to return (default: 5)"), livecrawl: z.enum(['always', 'fallback']).optional().describe("Livecrawl strategy: 'always' to always crawl live, 'fallback' to only crawl when index has no result") },

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

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