Skip to main content
Glama
Fabien-desablens

Google Search MCP Server

google_search

Perform Google searches using the official API with automatic key rotation, intelligent quota management, and advanced filtering options. Supports multi-language, geolocation, and SafeSearch for tailored results.

Instructions

Performs Google searches using the official API with automatic API key rotation.

Features:

  • Official Google Web Search

  • Automatic API key rotation

  • Intelligent quota management

  • Multi-language and geolocation support

Parameters:

  • query: Search query (required)

  • num: Number of results (1-10, default: 5)

  • start: Starting index (default: 1)

  • safe: SafeSearch (off/active, default: off)

  • lr: Language (ex: lang_fr, lang_en)

  • gl: Country (ex: fr, us, uk)

Returns a JSON list of results with title, link, description and domain.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
crNoCountry restriction (ex: countryFR, countryUS)
dateRestrictNoTime filter (ex: d1=24h, w1=week, m1=month, y1=year)
exactTermsNoExact phrase required
excludeTermsNoWords to exclude from search
fileTypeNoFile type (ex: pdf, doc, ppt)
glNoGeolocation (country code: fr, us, uk, etc.)
lrNoResults language (ex: lang_fr, lang_en)
numNoNumber of results to return (1-10)
orTermsNoAlternative terms (OR)
queryYesGoogle search query
rightsNoLicense filters (ex: cc_publicdomain)
safeNoSafeSearch level
searchTypeNoSearch type (value: image)
siteSearchNoSearch specific site (ex: reddit.com)
siteSearchFilterNoInclude (i) or exclude (e) the site
sortNoSort by date (value: date)
startNoStarting index of results

Implementation Reference

  • The core handler function 'execute' that implements the google_search tool logic: selects an available API key, constructs search parameters, calls Google Custom Search API, processes results into structured format, manages quotas, handles errors, and returns JSON response with results and metadata.
    export const execute = async (params: SearchParams) => { const response = { content: [] as TextContent[], isError: false }; try { const apiKey = config.getAvailableKey(); if (!apiKey) { throw new Error('All Google API keys have reached their daily quota. Try again tomorrow.'); } // Log which API key is being used console.error(`[INFO] Using API Key: ${apiKey.id} -> ${apiKey.apiKey.substring(0, 12)}...${apiKey.apiKey.substring(-4)}`); console.error(`[INFO] Search Engine ID: ${apiKey.searchEngineId}`); const customsearch = google.customsearch('v1'); const searchParams = { auth: apiKey.apiKey, cx: apiKey.searchEngineId, q: params.query, num: Math.min(params.num || 5, 10), start: params.start || 1, safe: params.safe || 'off', ...(params.lr && { lr: params.lr }), ...(params.gl && { gl: params.gl, hl: params.gl }), ...(params.dateRestrict && { dateRestrict: params.dateRestrict }), ...(params.fileType && { fileType: params.fileType }), ...(params.siteSearch && { siteSearch: params.siteSearch }), ...(params.siteSearchFilter && { siteSearchFilter: params.siteSearchFilter }), ...(params.cr && { cr: params.cr }), ...(params.exactTerms && { exactTerms: params.exactTerms }), ...(params.excludeTerms && { excludeTerms: params.excludeTerms }), ...(params.orTerms && { orTerms: params.orTerms }), ...(params.rights && { rights: params.rights }), ...(params.sort && { sort: params.sort }), ...(params.searchType && { searchType: params.searchType }), }; const searchResponse = await customsearch.cse.list(searchParams); config.incrementUsage(apiKey.id); const items = searchResponse.data.items || []; const results: SearchResult[] = items.map(item => ({ title: item.title || '', link: item.link || '', snippet: item.snippet || '', displayLink: item.displayLink || '', })); const quotaStatus = config.getQuotaStatus(); response.content.push({ type: 'text' as const, text: JSON.stringify({ results, metadata: { query: params.query, totalResults: searchResponse.data.searchInformation?.totalResults || '0', searchTime: searchResponse.data.searchInformation?.searchTime || '0', resultsCount: results.length, usedApiKey: apiKey.id, quotaStatus } }, null, 2), }); } catch (error: any) { console.error('[ERROR] Google Search Error:', error); if (error.code === 403) { const apiKey = config.getAvailableKey(); if (apiKey) { config.disableKey(apiKey.id, 'Quota exceeded or 403 error'); } } response.content.push({ type: 'text' as const, text: JSON.stringify({ error: 'Google Search error', message: error.message, details: error.code ? `Code: ${error.code}` : 'Unknown error', quotaStatus: config.getQuotaStatus() }, null, 2), }); response.isError = true; } return response; };
  • Zod input schema defining parameters for the google_search tool, including query (required), num results, pagination, safety, language, geolocation, and advanced search filters.
    export const inputSchema = z.object({ query: z.string().describe('Google search query'), num: z.number().int().min(1).max(10).optional().describe('Number of results to return (1-10)'), start: z.number().int().min(1).optional().describe('Starting index of results'), safe: z.enum(['off', 'active']).optional().describe('SafeSearch level'), lr: z.string().optional().describe('Results language (ex: lang_fr, lang_en)'), gl: z.string().optional().describe('Geolocation (country code: fr, us, uk, etc.)'), dateRestrict: z.string().optional().describe('Time filter (ex: d1=24h, w1=week, m1=month, y1=year)'), fileType: z.string().optional().describe('File type (ex: pdf, doc, ppt)'), siteSearch: z.string().optional().describe('Search specific site (ex: reddit.com)'), siteSearchFilter: z.enum(['i', 'e']).optional().describe('Include (i) or exclude (e) the site'), cr: z.string().optional().describe('Country restriction (ex: countryFR, countryUS)'), exactTerms: z.string().optional().describe('Exact phrase required'), excludeTerms: z.string().optional().describe('Words to exclude from search'), orTerms: z.string().optional().describe('Alternative terms (OR)'), rights: z.string().optional().describe('License filters (ex: cc_publicdomain)'), sort: z.string().optional().describe('Sort by date (value: date)'), searchType: z.string().optional().describe('Search type (value: image)'), });
  • Tool name definition: 'google_search', used for identification and registration in the MCP server.
    export const name = 'google_search';
  • src/server.ts:20-22 (registration)
    Registration loop that adds the google_search tool (imported via tools/index.js) to the MCP server using server.tool() with its name, description, schema, annotations, and execute handler.
    for (const tool of Object.values(tools)) { server.tool(tool.name, tool.description, tool.inputSchema.shape, tool.annotations, tool.execute); }
  • GoogleSearchConfig class providing API key management, quota tracking, key selection, and status reporting, used by the handler for key rotation and error handling.
    export class GoogleSearchConfig { private globalConfig: GlobalConfigManager; constructor() { this.globalConfig = new GlobalConfigManager(); this.checkConfiguration(); } private checkConfiguration(): void { if (!this.globalConfig.hasValidConfig()) { console.error('[WARN] No Google API keys configured.'); console.error('[INFO] First time setup:'); console.error('[INFO] 1. Get API keys: https://console.cloud.google.com/'); console.error('[INFO] 2. Get Search Engine ID: https://programmablesearchengine.google.com/'); console.error('[INFO] 3. Run setup command: npx @kyaniiii/google-search-mcp setup'); console.error('[INFO] Full documentation: https://github.com/Fabien-desablens/google-search-mcp#readme'); return; } const status = this.globalConfig.getQuotaStatus(); console.error(`[INFO] ${status.keysStatus.length} Google API keys loaded globally`); } public getAvailableKey(): GoogleApiKey | null { const key = this.globalConfig.getAvailableKey(); if (!key) return null; return { id: key.id, apiKey: key.apiKey, searchEngineId: key.searchEngineId, dailyUsage: key.dailyUsage, dailyLimit: key.dailyLimit, lastReset: key.lastReset, isActive: key.isActive }; } public incrementUsage(keyId: string): void { this.globalConfig.incrementUsage(keyId); } public getQuotaStatus(): { totalUsed: number; totalLimit: number; keysStatus: any[] } { return this.globalConfig.getQuotaStatus(); } public disableKey(keyId: string, reason: string): void { this.globalConfig.disableKey(keyId, reason); } public hasValidConfig(): boolean { return this.globalConfig.hasValidConfig(); } public getConfigPath(): string { return this.globalConfig.getConfigPath(); } }
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/Fabien-desablens/google-search-mcp'

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