This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a Model Context Protocol (MCP) server that provides AI-powered Google search using Gemini with search grounding. It enables Claude Desktop and other MCP clients to perform real-time web searches with AI-synthesized answers and citations.
Key Technologies:
- MCP SDK (
@modelcontextprotocol/sdk) for stdio server transport and JSON-RPC 2.0 communication - Google Generative AI SDK (
@google/generative-ai) for Gemini API integration - Node.js native test runner for unit/integration testing
- ES modules (
type: "module"in package.json)
npm start # Start MCP server (runs check-env first)
npm run dev # Auto-reload mode with --watch flagnpm test # Unit tests only (test/unit/**/*.test.js)
npm run test:integration # Integration test with live Gemini API
npm run test:all # All tests (unit + integration)Running a single test file:
node --test test/unit/tool-handler.test.jsnpm run check-env # Validate .env configuration
npm run security:audit # Check for vulnerabilities
npm run security:fix # Auto-fix security issues
npm run security:update # Update deps and auditThe server implements a single-tool MCP server following the stdio transport pattern:
-
Server Initialization (src/index.js:117-128)
- Creates MCP Server instance with name/version metadata
- Declares
toolscapability - Connects to StdioServerTransport for JSON-RPC communication
-
Tool Registration (src/index.js:131-176)
- Handles
ListToolsRequestSchemato expose theask_googletool - Tool description is optimized for AI agent invocation with clear trigger phrases
- Input schema includes validation rules and examples
- Handles
-
Request Handling (src/index.js:179-326)
- Handles
CallToolRequestSchemafor tool execution - Input validation: null/undefined check → type check → empty string check → length check (max 10,000 chars)
- Gemini integration: Uses
getGenerativeModel()withtools: [{ googleSearch: {} }]for search grounding - Response formatting: Extracts grounding metadata (sources, search queries), caps at 12 sources and 8 queries, then appends to response text
- Error categorization: Maps Gemini errors to MCP-friendly error codes (AUTH_ERROR, QUOTA_ERROR, TIMEOUT_ERROR, API_ERROR)
- Handles
-
Process Stability (src/index.js:328-360)
- Handles unhandled rejections, uncaught exceptions, SIGINT, SIGTERM
- All failures trigger clean shutdown with error logging to stderr
Model selection (src/index.js:226-231):
- Supports three models via the
modelparameter:pro(default),flash,flash-lite - Model map:
pro→gemini-3.1-pro-preview,flash→gemini-3-flash-preview,flash-lite→gemini-3.1-flash-lite-preview - Model is configured with
systemInstructionloaded fromsrc/system-prompt.txt(cached at startup, date injected per request as ISO-8601) - System prompt optimized for AI-to-AI communication (terse, direct, no conversational fluff)
Search grounding (src/index.js:238-242, 250-268):
- Enabled via
tools: [{ googleSearch: {} }]in model config - Returns
groundingMetadatawith source URLs and search queries performed - Sources are deduplicated by URL, filtered for empty URLs, and capped at 12 to prevent bloat
- Search queries are capped at 8
- Sources and queries formatted as markdown and appended to response
src/system-prompt.txt defines Gemini's response behavior:
- Loaded once at startup and cached (line 63), only
{{CURRENT_DATE}}substituted per request (line 236-237) - Current date injected as ISO-8601:
YYYY-MM-DD (UTC)for unambiguous parsing - Optimized for AI-to-AI communication (no pleasantries, conversational filler, or marketing language)
- Enforces information density and machine readability
- Explicit anti-patterns list using "Do not X" per line (not "Do not:" header) for better LLM compliance
- Instructs Gemini not to add its own "Sources" section (server appends authoritative sources list)
- Guides response length, provenance flagging, code inclusion, and formatting choices
scripts/check-env.js provides comprehensive validation:
- Checks
.envfile existence in both project root and home directory (~/.env), matching runtime precedence - Validates
GOOGLE_API_KEY(not placeholder, minimum length) - Masks API key in output (shows length, not prefix, to prevent leaks)
- Reports optional vars (
NODE_ENV) with defaults - Validates Node.js version against
package.jsonengines requirement - Exits with code 1 on failure (blocks
npm startvia prestart hook)
Unit tests (test/unit/tool-handler.test.js):
- Mock
MockGenerativeModelclass simulates Gemini API responses - Extracted
handleAskGoogle()function mirrors production logic for testability - Test categories: input validation, successful responses, error handling, edge cases, retry logic, output_file parameter, model parameter
- 64 total test cases covering all validation rules, error scenarios, retry behavior, and file output
Integration test (test/test-gemini-mcp.js):
- Tests live Gemini API with real
GOOGLE_API_KEYfrom.env - Validates actual search grounding, source extraction, error handling
- Uses MCP protocol handshake (initialize → initialized → tools/list → tools/call)
All errors are categorized by prefix for MCP client consumption:
[AUTH_ERROR]: API key issues (triggers: "api key", "unauthorized", "permission", "401", "403")[QUOTA_ERROR]: Rate limits or quota exceeded (triggers: "quota", "rate limit", "429", "resource exhausted")[TIMEOUT_ERROR]: Request timeouts (triggers: "timeout")[API_ERROR]: Generic Gemini API errors (fallback for all other errors)
Error detection uses case-insensitive string matching on error.message.toLowerCase().
Retry logic (src/index.js:76-115):
- Retries up to 3 times with exponential backoff (1s, 2s, 4s)
- AUTH_ERROR and QUOTA_ERROR are not retried (permanent failures)
- All other errors are retried
- Retry logging shows correct attempt count: "Attempt X/(N+1)"
The output_file parameter (src/index.js:154-162, 287-299):
- Accepts both absolute and relative paths
- Relative paths resolve from the current working directory (
process.cwd()), not "project root" - Parent directories are created automatically if needed (
mkdirSyncwithrecursive: true) - File write errors are non-fatal: response is still returned with error message appended
- Successfully written files are logged to stderr:
[FILE_OUTPUT] Successfully wrote response to: {path}
Responses follow this structure:
[AI-generated answer]
---
**Sources:**
1. [Title](URL)
2. [Title](URL)
**Search queries performed:**
1. "query text"
2. "query text"
Sources and search queries are optional (only included if grounding metadata exists). Sources are capped at 12 and search queries at 8 to prevent bloated responses.
NPM package (@gpriday/ask-google-mcp):
- Scoped package requires
--access publicfor publishing - Binary:
ask-google-mcp(defined in package.json bin field) - Published files:
src/,scripts/,README.md,LICENSE,CHANGELOG.md,.env.example - Use
package-lock.jsonis committed for reproducible builds - CI/CD should use
npm ci(notnpm install)
The server supports three models selectable per-query via the model parameter:
pro(default) →gemini-3.1-pro-preview— Advanced reasoning with search groundingflash→gemini-3-flash-preview— Fast and cost-effective for simple lookupsflash-lite→gemini-3.1-flash-lite-preview— Fastest and cheapest for simple factual queries- Model map is defined in
src/index.jsin themodelMapobject
Use /release [patch|minor|major] slash command for automated releases:
- Auto-detects version bump from Conventional Commits if no argument provided
- Validates tests pass, git is clean, NPM authentication
- Updates package.json version
- Creates git commit, tag, publishes to NPM, pushes to origin
- All-or-nothing process (stops on any validation failure)