Skip to main content
Glama
tanamurayuuki

Gemini URL Context & Search MCP Server

GoogleSearchGenAI.ts3.38 kB
import { UpstreamUnavailableError, SafetyBlockedError, RateLimitError } from '../domain/DomainError.js'; export interface GoogleSearchInput { query: string; instruction?: string; model: string; } export interface GoogleSearchResponse { result: string; searchQueries: string[]; sources: Array<{ title: string; url: string; }>; } export class GoogleSearchGenAI { constructor(private readonly apiKey: string) {} async search(input: GoogleSearchInput): Promise<GoogleSearchResponse> { const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(input.model)}:generateContent`; const promptText = `Role: You are a meticulous web researcher. Primary directive: - Perform grounded Google Search and for ANY URL you cite, you MUST fetch it via URL Context and synthesize findings. - Prefer authoritative, up-to-date sources. - If coverage is insufficient, refine the query and continue internally up to 5 rounds. Stop once adequate. Task:${input.instruction ? `\n${input.instruction}` : ''} Research focus: ${input.query}`; const tools = [{ google_search: {} }, { url_context: {} }]; const body = { contents: [ { parts: [{ text: promptText }], }, ], tools, }; try { const response = await fetch(endpoint + `?key=${encodeURIComponent(this.apiKey)}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (!response.ok) { const text = await response.text(); this.mapAndThrowError(new Error(`Gemini API error ${response.status}: ${text}`)); } const json = await response.json(); return this.parseSearchResponse(json); } catch (error) { this.mapAndThrowError(error as Error); throw error; // This won't be reached due to mapAndThrowError throwing } } private parseSearchResponse(json: any): GoogleSearchResponse { const candidate = json?.candidates?.[0]; const textOut = candidate?.content?.parts?.map((p: any) => p?.text).filter(Boolean).join("\n") || ""; // Collect grounding metadata for search results const groundingMeta = candidate?.grounding_metadata; const searchQueries = groundingMeta?.web_search_queries || []; const groundingChunks = groundingMeta?.grounding_chunks || []; const sources = groundingChunks.map((chunk: any) => ({ title: chunk.web?.title || "(no title)", url: chunk.web?.uri || "(no URL)" })); return { result: textOut, searchQueries, sources }; } private mapAndThrowError(error: Error): void { const errorMessage = error.message.toLowerCase(); if (errorMessage.includes('safety') || errorMessage.includes('blocked')) { throw new SafetyBlockedError(error.message); } if (errorMessage.includes('rate limit') || errorMessage.includes('quota')) { throw new RateLimitError(error.message); } if (errorMessage.includes('network') || errorMessage.includes('timeout') || errorMessage.includes('unavailable') || errorMessage.includes('connection')) { throw new UpstreamUnavailableError(error.message, error); } throw new UpstreamUnavailableError(`Gemini API error: ${error.message}`, error); } }

Implementation Reference

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/tanamurayuuki/MCP-URLcontext'

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