Skip to main content
Glama

Brave Search MCP Server

Official
index.ts3.75 kB
import type { Endpoints } from './types.js'; import config from '../config.js'; import { stringify } from '../utils.js'; const typeToPathMap: Record<keyof Endpoints, string> = { images: '/res/v1/images/search', localPois: '/res/v1/local/pois', localDescriptions: '/res/v1/local/descriptions', news: '/res/v1/news/search', videos: '/res/v1/videos/search', web: '/res/v1/web/search', summarizer: '/res/v1/summarizer/search', }; const getDefaultRequestHeaders = (): Record<string, string> => { return { Accept: 'application/json', 'Accept-Encoding': 'gzip', 'X-Subscription-Token': config.braveApiKey, }; }; const isValidGoggleURL = (url: string) => { try { // Only allow HTTPS URLs return new URL(url).protocol === 'https:'; } catch { return false; } }; async function issueRequest<T extends keyof Endpoints>( endpoint: T, parameters: Endpoints[T]['params'], // TODO (Sampson): Implement support for custom request headers (helpful for POIs, etc.) requestHeaders: Endpoints[T]['requestHeaders'] = {} as Endpoints[T]['requestHeaders'] ): Promise<Endpoints[T]['response']> { // TODO (Sampson): Improve rate-limit logic to support self-throttling and n-keys // checkRateLimit(); // Determine URL, and setup parameters const url = new URL(`https://api.search.brave.com${typeToPathMap[endpoint]}`); const queryParams = new URLSearchParams(); // TODO (Sampson): Move param-construction/validation to modules for (const [key, value] of Object.entries(parameters)) { // The 'ids' parameter is expected to appear multiple times for multiple IDs if (['localPois', 'localDescriptions'].includes(endpoint)) { if (key === 'ids') { if (Array.isArray(value) && value.length > 0) { value.forEach((id) => queryParams.append(key, id)); } else if (typeof value === 'string') { queryParams.set(key, value); } continue; } } // Handle `result_filter` parameter if (key === 'result_filter') { // Handle special behavior of 'summary' parameter: // Requires `result_filter` to be empty, or only contain 'summarizer' // see: https://bravesoftware.slack.com/archives/C01NNFM9XMM/p1751654841090929 if ('summary' in parameters && parameters.summary === true) { queryParams.set(key, 'summarizer'); } else if (Array.isArray(value) && value.length > 0) { queryParams.set(key, value.join(',')); } continue; } // Handle `goggles` parameter(s) if (key === 'goggles') { if (typeof value === 'string') { queryParams.set(key, value); } else if (Array.isArray(value)) { for (const url of value.filter(isValidGoggleURL)) { queryParams.append(key, url); } } continue; } if (value !== undefined) { queryParams.set(key === 'query' ? 'q' : key, value.toString()); } } // Issue Request const urlWithParams = url.toString() + '?' + queryParams.toString(); const headers = { ...getDefaultRequestHeaders(), ...requestHeaders } as Headers; const response = await fetch(urlWithParams, { headers }); // Handle Error if (!response.ok) { let errorMessage = `${response.status} ${response.statusText}`; try { const responseBody = await response.json(); errorMessage += `\n${stringify(responseBody, true)}`; } catch (error) { errorMessage += `\n${await response.text()}`; } // TODO (Sampson): Setup proper error handling, updating state, etc. throw new Error(errorMessage); } // Return Response const responseBody = await response.json(); return responseBody as Endpoints[T]['response']; } export default { issueRequest, };

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/brave/brave-search-mcp-server'

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