brave_local_search
Search for local businesses and places using Brave's Local Search API to find detailed information including business names, addresses, ratings, phone numbers, and opening hours for location-based queries.
Instructions
Brave Local Search API provides enrichments for location search results. Access to this API is available only through the Brave Search API Pro plans; confirm the user's plan before using this tool (if the user does not have a Pro plan, use the brave_web_search tool). Searches for local businesses and places using Brave's Local Search API. Best for queries related to physical locations, businesses, restaurants, services, etc.
Returns detailed information including:
- Business names and addresses
- Ratings and review counts
- Phone numbers and opening hours
Use this when the query implies 'near me', 'in my area', or mentions specific locations (e.g., 'in San Francisco'). This tool automatically falls back to brave_web_search if no local results are found.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Number of results (1-20, default 10). Applies only to web search results (i.e., has no effect on locations, news, videos, etc.) | |
| country | No | Search query country, where the results come from. The country string is limited to 2 character country codes of supported countries. | US |
| extra_snippets | No | A snippet is an excerpt from a page you get as a result of the query, and extra_snippets allow you to get up to 5 additional, alternative excerpts. Only available under Free AI, Base AI, Pro AI, Base Data, Pro Data and Custom plans. | |
| freshness | No | Filters search results by when they were discovered. The following values are supported: 'pd' - Discovered within the last 24 hours. 'pw' - Discovered within the last 7 days. 'pm' - Discovered within the last 31 days. 'py' - Discovered within the last 365 days. 'YYYY-MM-DDtoYYYY-MM-DD' - Timeframe is also supported by specifying the date range e.g. 2022-04-01to2022-07-30. | |
| goggles | No | Goggles act as a custom re-ranking on top of Brave's search index. The parameter supports both a url where the Goggle is hosted or the definition of the Goggle. For more details, refer to the Goggles repository (i.e., https://github.com/brave/goggles-quickstart). | |
| offset | No | Pagination offset (max 9, default 0) | |
| query | Yes | Search query (max 400 chars, 50 words) | |
| result_filter | No | Result filter (default ['web', 'query']) | |
| safesearch | No | Filters search results for adult content. The following values are supported: 'off' - No filtering. 'moderate' - Filters explicit content (e.g., images and videos), but allows adult domains in search results. 'strict' - Drops all adult content from search results. The default value is 'moderate'. | moderate |
| search_lang | No | Search language preference. The 2 or more character language code for which the search results are provided. | en |
| spellcheck | No | Whether to spellcheck the provided query. | |
| summary | No | This parameter enables summary key generation in web search results. This is required for summarizer to be enabled. | |
| text_decorations | No | Whether display strings (e.g. result snippets) should include decoration markers (e.g. highlighting characters). | |
| ui_lang | No | The language of the UI. The 2 or more character language code for which the search results are provided. | en-US |
| units | No | The measurement units. If not provided, units are derived from search country. |
Implementation Reference
- src/tools/local/index.ts:34-74 (handler)The main handler function 'execute' that implements the logic for brave_local_search. It performs a web search to find locations, fetches descriptions if available, formats results, and falls back to web search if no local results.export const execute = async (params: WebQueryParams) => { // Make sure both 'web' and 'locations' are in the result_filter params = { ...params, result_filter: [...(params.result_filter || []), 'web', 'locations'] }; // Starts with a web search to retrieve potential location IDs const { locations, web: web_fallback } = await API.issueRequest<'web'>('web', params); // We can send up to 20 location IDs at a time to the Local API // TODO (Sampson): Add support for multiple requests const locationIDs = (locations?.results || []).map((poi) => poi.id as string).slice(0, 20); // No locations were found - user's plan may not include access to the Local API if (!locations || locationIDs.length === 0) { // If we have web results, but no locations, we'll fall back to the web results if (web_fallback && web_fallback.results.length > 0) { return buildFallbackWebResponse(web_fallback); } // If we have no web results, we'll send a message to the user return { content: [ { type: 'text' as const, text: "No location data was returned. User's plan does not support local search, or the query may be unclear.", }, ], }; } // Fetch AI-generated descriptions const descriptions = await API.issueRequest<'localDescriptions'>('localDescriptions', { ids: locationIDs, }); return { content: formatLocalResults(locations.results, descriptions.results).map((formattedPOI) => ({ type: 'text' as const, text: formattedPOI, })), }; };
- src/tools/web/params.ts:3-241 (schema)Zod input schema for the tool parameters (reused from web search params), defining query, country, language, count, filters etc. Referenced as webParams.shape in registration.export const params = z.object({ query: z .string() .max(400) .refine((str) => str.split(/\s+/).length <= 50, 'Query cannot exceed 50 words') .describe('Search query (max 400 chars, 50 words)'), country: z .enum([ 'ALL', 'AR', 'AU', 'AT', 'BE', 'BR', 'CA', 'CL', 'DK', 'FI', 'FR', 'DE', 'HK', 'IN', 'ID', 'IT', 'JP', 'KR', 'MY', 'MX', 'NL', 'NZ', 'NO', 'CN', 'PL', 'PT', 'PH', 'RU', 'SA', 'ZA', 'ES', 'SE', 'CH', 'TW', 'TR', 'GB', 'US', ]) .default('US') .describe( 'Search query country, where the results come from. The country string is limited to 2 character country codes of supported countries.' ) .optional(), search_lang: z .enum([ 'ar', 'eu', 'bn', 'bg', 'ca', 'zh-hans', 'zh-hant', 'hr', 'cs', 'da', 'nl', 'en', 'en-gb', 'et', 'fi', 'fr', 'gl', 'de', 'gu', 'he', 'hi', 'hu', 'is', 'it', 'jp', 'kn', 'ko', 'lv', 'lt', 'ms', 'ml', 'mr', 'nb', 'pl', 'pt-br', 'pt-pt', 'pa', 'ro', 'ru', 'sr', 'sk', 'sl', 'es', 'sv', 'ta', 'te', 'th', 'tr', 'uk', 'vi', ]) .default('en') .describe( 'Search language preference. The 2 or more character language code for which the search results are provided.' ) .optional(), ui_lang: z .enum([ 'es-AR', 'en-AU', 'de-AT', 'nl-BE', 'fr-BE', 'pt-BR', 'en-CA', 'fr-CA', 'es-CL', 'da-DK', 'fi-FI', 'fr-FR', 'de-DE', 'zh-HK', 'en-IN', 'en-ID', 'it-IT', 'ja-JP', 'ko-KR', 'en-MY', 'es-MX', 'nl-NL', 'en-NZ', 'no-NO', 'zh-CN', 'pl-PL', 'en-PH', 'ru-RU', 'en-ZA', 'es-ES', 'sv-SE', 'fr-CH', 'de-CH', 'zh-TW', 'tr-TR', 'en-GB', 'en-US', 'es-US', ]) .default('en-US') .describe( 'The language of the UI. The 2 or more character language code for which the search results are provided.' ) .optional(), count: z .number() .int() .min(1) .max(20) .default(10) .describe( 'Number of results (1-20, default 10). Applies only to web search results (i.e., has no effect on locations, news, videos, etc.)' ) .optional(), offset: z .number() .int() .min(0) .max(9) .default(0) .describe('Pagination offset (max 9, default 0)') .optional(), safesearch: z .enum(['off', 'moderate', 'strict']) .default('moderate') .describe( "Filters search results for adult content. The following values are supported: 'off' - No filtering. 'moderate' - Filters explicit content (e.g., images and videos), but allows adult domains in search results. 'strict' - Drops all adult content from search results. The default value is 'moderate'." ) .optional(), freshness: z .enum(['pd', 'pw', 'pm', 'py', 'YYYY-MM-DDtoYYYY-MM-DD']) .describe( "Filters search results by when they were discovered. The following values are supported: 'pd' - Discovered within the last 24 hours. 'pw' - Discovered within the last 7 days. 'pm' - Discovered within the last 31 days. 'py' - Discovered within the last 365 days. 'YYYY-MM-DDtoYYYY-MM-DD' - Timeframe is also supported by specifying the date range e.g. 2022-04-01to2022-07-30." ) .optional(), text_decorations: z .boolean() .default(true) .describe( 'Whether display strings (e.g. result snippets) should include decoration markers (e.g. highlighting characters).' ) .optional(), spellcheck: z .boolean() .default(true) .describe('Whether to spellcheck the provided query.') .optional(), result_filter: z .array( z.enum([ 'discussions', 'faq', 'infobox', 'news', 'query', 'summarizer', 'videos', 'web', 'locations', 'rich', ]) ) .default(['web', 'query']) .describe("Result filter (default ['web', 'query'])") .optional(), goggles: z .array(z.string()) .describe( "Goggles act as a custom re-ranking on top of Brave's search index. The parameter supports both a url where the Goggle is hosted or the definition of the Goggle. For more details, refer to the Goggles repository (i.e., https://github.com/brave/goggles-quickstart)." ) .optional(), units: z .union([z.literal('metric'), z.literal('imperial')]) .describe('The measurement units. If not provided, units are derived from search country.') .optional(), extra_snippets: z .boolean() .describe( 'A snippet is an excerpt from a page you get as a result of the query, and extra_snippets allow you to get up to 5 additional, alternative excerpts. Only available under Free AI, Base AI, Pro AI, Base Data, Pro Data and Custom plans.' ) .optional(), summary: z .boolean() .describe( 'This parameter enables summary key generation in web search results. This is required for summarizer to be enabled.' ) .optional(), });
- src/tools/local/index.ts:76-87 (registration)The registration function that registers the 'brave_local_search' tool with the MCP server using name, description, inputSchema, annotations, and the execute handler.export const register = (mcpServer: McpServer) => { mcpServer.registerTool( name, { title: name, description: description, inputSchema: webParams.shape, annotations: annotations, }, execute ); };
- src/tools/local/index.ts:111-127 (helper)Helper function to format POI (points of interest) data from local search into readable string summaries using stringify.const formatLocalResults = ( poisData: LocationResult[], descData: LocationDescription[] = [] ): string[] => { return poisData.map((poi) => { return stringify({ name: poi.title, price_range: poi.price_range, phone: poi.contact?.telephone, rating: poi.rating?.ratingValue, hours: formatOpeningHours(poi.opening_hours), rating_count: poi.rating?.reviewCount, description: descData.find(({ id }) => id === poi.id)?.description, address: poi.postal_address?.displayAddress, }); }); };