mcp-gemini-google-search
An MCP stdio server for Google-grounded Gemini answers and asynchronous Gemini Deep Research reports.
This repository ports the behavior of yukukotani/mcp-gemini-google-search and extends it with Deep Research. The upstream project publishes the mcp-gemini-google-search npm package; this Python port shares the name but installs from this Git repository rather than from a package index. The Python implementation uses the official google-genai SDK for Gemini API and Vertex AI access and the official MCP Python SDK low-level server.
Features
- Always advertises three standard tools:
google_search,deep_research, anddeep_research_result - Runs Google Search grounding through the Interactions API
- Optionally enables URL context and code execution per server or per search request
- Sends every
google_searchas an independent interaction withstore=false - Starts Deep Research as a provider-managed background interaction and returns a durable
interaction_idimmediately - Returns readable text content plus schema-backed
structuredContent; failures are readable MCP tool errors rather than opaque protocol errors - Formats successful answers and reports as GitHub-flavored Markdown with inline
[n]markers and a linked## Sourceslist - Supports Google AI Studio and Vertex AI
Choose a tool
| Need | Tool | Behavior |
|---|---|---|
| A current fact, focused lookup, URL inspection, or computed answer | google_search |
Returns one grounded answer synchronously. Refine and call again if needed. |
| A comprehensive comparison, survey, or long report | deep_research |
Starts one billed background run and returns its interaction_id; it does not wait for the report. |
| Progress or the final report for an existing run | deep_research_result |
Fetches or long-polls by interaction_id; it never starts another research run. |
Requirements
- Python 3.14 or later
mcp2.2.0 or later (MCP Python SDK v2)
Installation
Install the server as a uv tool from this repository:
uv tool install git+https://github.com/zchee/mcp-gemini-google-search
Or run it without installing using uvx:
uvx --from git+https://github.com/zchee/mcp-gemini-google-search mcp-gemini-google-search
Alternatively, install with pip:
pip install git+https://github.com/zchee/mcp-gemini-google-search
Bundled plugin MCP configuration
Both bundled plugins register the server from the same .mcp.json at the repository root:
{
"mcpServers": {
"mcp-gemini-google-search": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/zchee/mcp-gemini-google-search",
"mcp-gemini-google-search"
],
"env": {
"MCP_GEMINI_GOOGLE_API_KEY": "${user_config.GOOGLE_API_KEY}",
"MCP_GEMINI_GEMINI_API_KEY": "${user_config.GEMINI_API_KEY}"
},
"env_vars": [
"GOOGLE_API_KEY",
"GEMINI_API_KEY"
],
"startup_timeout_sec": 60
}
}
}
The env block fills the server's own MCP_GEMINI_ namespace from the plugin configuration Claude Code collects when the plugin is enabled, so the key lives in the OS keychain instead of a shell profile. env_vars states the same intent in the spelling Codex understands: Codex withholds the host environment from the servers it spawns unless a variable is named there, and it forwards these two under their own names. startup_timeout_sec is a Codex-only field that Claude Code ignores.
Codex CLI substitutes neither ${VAR} nor ${user_config.VAR} in an MCP env table — it passes both entries through verbatim — so the server discards them as unexpanded (see Environment reference) and takes the key from env_vars, which means exporting GOOGLE_API_KEY or GEMINI_API_KEY in the shell that launches Codex is enough. $CODEX_HOME/.env (default ~/.codex/.env) remains the way to keep the key out of the shell altogether; the process environment wins when a name is set in both — see Codex dotenv file.
Bundled Claude Code plugin
This repository is also a Claude Code plugin (.claude-plugin/plugin.json at the root). It registers the MCP server and three skills — gemini-google-search, gemini-deep-research, and gemini-deep-research-result — that teach the client when to search versus research, how to relay answers without dropping citations, and how to poll a Deep Research run without starting a duplicate billed run.
Install it from this repository's marketplace inside Claude Code:
/plugin marketplace add zchee/mcp-gemini-google-search
/plugin install mcp-gemini-google-search@mcp-gemini-google-search
Or load it for a single session without installing:
claude --plugin-dir .
The plugin starts the server with uvx from this repository, so uv must be on PATH. Claude Code asks for the two API keys when the plugin is enabled and keeps a filled value in the OS keychain; leaving both empty falls back to the variables from Configuration exported in the shell that launches Claude Code.
${user_config.KEY} resolves to the value stored for that field of the plugin configuration, declared under userConfig in .claude-plugin/plugin.json; Claude Code also substitutes ${VAR} and ${VAR:-default} for ordinary environment variables, and passes the VS Code spelling ${env:GOOGLE_API_KEY} through as literal text. A field left empty leaves the reference unexpanded, and the server discards it rather than reading it as a key, so the inherited GOOGLE_API_KEY or GEMINI_API_KEY takes over. Export those two unprefixed names rather than their MCP_GEMINI_ variants: an env entry replaces the inherited variable of the same name, so a shell-exported MCP_GEMINI_GOOGLE_API_KEY is overwritten by whatever the user_config reference resolves to.
On a cold uv cache the first launch clones and builds the package, which can exceed Claude Code's default MCP startup timeout (Claude Code ignores the Codex-only startup_timeout_sec field). If the server fails to start once, launch again — the build is cached — or raise the timeout with MCP_TIMEOUT=60000 claude.
Configuration
Google AI Studio
Set either GEMINI_API_KEY or GOOGLE_API_KEY. When both are set, GOOGLE_API_KEY takes precedence.
export GEMINI_API_KEY="your-api-key"
export GEMINI_MODEL="gemini-3.1-pro-preview" # optional
Vertex AI
export GOOGLE_GENAI_USE_VERTEXAI="true"
export GOOGLE_CLOUD_PROJECT="your-gcp-project" # required
export GOOGLE_CLOUD_LOCATION="global" # optional, defaults to "global"
export GEMINI_MODEL="gemini-3.1-pro-preview" # optional
GOOGLE_GENAI_USE_VERTEXAI is treated as enabled when set to one of 1, true, yes, or on (case-insensitive). GOOGLE_CLOUD_PROJECT is required in this mode.
If no model is configured, the server defaults to gemini-3.1-pro-preview.
Codex dotenv file
At startup the server parses $CODEX_HOME/.env — ~/.codex/.env when CODEX_HOME is unset or blank — with python-dotenv and loads recognized entries into its own environment. Variables already exported with a usable value always take precedence, and a missing file is a silent no-op. Codex CLI reads its dotenv file for itself but does not pass it to the MCP servers it spawns, so storing GEMINI_API_KEY there is enough for both Codex CLI and this server — no env table is needed in config.toml. If you relocate the directory via CODEX_HOME, make sure that variable also reaches the server — export it, or set it in the client's env configuration.
Environment reference
| Variable | Default | Behavior |
|---|---|---|
GOOGLE_API_KEY |
none | Google AI Studio key; takes precedence over GEMINI_API_KEY. |
GEMINI_API_KEY |
none | Google AI Studio key used when GOOGLE_API_KEY is empty. |
GEMINI_MODEL |
gemini-3.1-pro-preview |
Model used by google_search. |
GOOGLE_GENAI_USE_VERTEXAI |
disabled | Selects Vertex AI when set to a recognized truthy value. |
GOOGLE_CLOUD_PROJECT |
none | Required when Vertex AI is enabled. |
GOOGLE_CLOUD_LOCATION |
global |
Vertex AI location. |
GEMINI_ENABLE_URL_CONTEXT |
disabled | Server default for URL context on google_search. |
GEMINI_ENABLE_CODE_EXECUTION |
disabled | Server default for code execution on google_search. |
GEMINI_DEEP_RESEARCH_AGENT |
deep-research-preview-04-2026 |
Server default for deep_research. |
GEMINI_SERVICE_TIER |
none | Service tier (flex, standard, priority) for both tools. |
CODEX_HOME |
~/.codex |
Dotenv directory parsed at startup; exported variables win. |
Every variable above is also recognized with an MCP_GEMINI_ prefix — for example MCP_GEMINI_GEMINI_API_KEY or MCP_GEMINI_GEMINI_MODEL. A non-blank prefixed variable takes precedence over its unprefixed name, so this server can be configured independently of other tools that read the shared names. For the API keys, both prefixed keys take precedence over both unprefixed ones: MCP_GEMINI_GOOGLE_API_KEY > MCP_GEMINI_GEMINI_API_KEY > GOOGLE_API_KEY > GEMINI_API_KEY.
Precedence resolves per name first, then per source. "Exported variables win" applies to the exact same name: an exported variable beats that name in the dotenv file. Across aliases the namespace always wins — an exported GOOGLE_API_KEY loses to an MCP_GEMINI_GEMINI_API_KEY set in the dotenv file. The effective order for every setting is MCP_GEMINI_<NAME> (exported > dotenv) before <NAME> (exported > dotenv).
Before any of that is resolved, a recognized variable whose value is empty, whitespace only, or nothing but a single unexpanded client placeholder such as ${GOOGLE_API_KEY:-} or ${user_config.GOOGLE_API_KEY} is removed from the environment. MCP clients disagree here — Claude Code substitutes ${VAR} in .mcp.json and writes an empty string when the source variable is unset, while Codex CLI passes the text through unchanged — and both spellings mean "the host had nothing to give". Dropping them keeps a shared client config additive: a placeholder is never used as a literal API key, and an empty entry never shadows the same name in $CODEX_HOME/.env. The rule is deliberately narrow, so a value that merely contains braces, such as AIza-${literal}-key, is kept verbatim.
The dotenv file is imported with least privilege: only the configuration names in the table above (and their MCP_GEMINI_ variants) are read from $CODEX_HOME/.env. Every other entry is ignored, ${VAR} interpolation is not applied, and exporting python-dotenv's PYTHON_DOTENV_DISABLED=1 disables the import. CODEX_HOME and MCP_GEMINI_CODEX_HOME are loader inputs rather than importable settings — the path is resolved from the process environment before the file is parsed, so set them as exported variables or in the client's env configuration, never inside the dotenv file.
Optional built-in tools
The Gemini url_context and code_execution built-in tools can be enabled alongside Google Search grounding. The environment variables below set the server-wide defaults, which individual google_search requests can override for one call. Both defaults are disabled when the variables are unset:
export GEMINI_ENABLE_URL_CONTEXT="1" # let the model fetch URLs mentioned in the query
export GEMINI_ENABLE_CODE_EXECUTION="1" # let the model run Python for computational answers
Both flags accept the same truthy spellings as GOOGLE_GENAI_USE_VERTEXAI (1, true, yes, on; case-insensitive). Enabled tools can increase token usage, and on Gemini 3 models Google Search grounding is billed per executed search query.
Optionally pin the Interactions API service tier for both google_search and deep_research:
export GEMINI_SERVICE_TIER="flex" # or standard, priority
Running
Run the server over stdio:
mcp-gemini-google-search
Optional file logging. Logs are written only to the given file (stdout is reserved for the MCP protocol):
mcp-gemini-google-search --logpath /tmp/mcp-gemini-google-search.log
Both -logpath and --logpath are accepted. Without this option the server does not emit routine logs; fatal startup errors still go to stderr.
Deep Research
The server uses deep-research-preview-04-2026 by default. Select the more exhaustive Max variant with:
export GEMINI_DEEP_RESEARCH_AGENT="deep-research-max-preview-04-2026"
Deep Research uses a two-tool start/poll workflow. deep_research starts a background run and immediately returns its interaction_id and initial status; it never waits for the multi-minute run to finish. Save that id before doing anything else: it is the only handle accepted by deep_research_result.
deep_research(query)
|
v
interaction_id + initial status
|
v
deep_research_result(interaction_id, wait_seconds=60)
|
+-- non-terminal status -> poll the same interaction_id again
+-- completed -> Markdown text + optional sources
`-- failed/cancelled -> MCP tool error; stop polling
deep_research_result defaults to wait_seconds=0, which performs one immediate status fetch. Use wait_seconds=60 while waiting for completion: the server rechecks the interaction about every five seconds for up to one minute, reducing client round-trips. Any non-terminal response contains interaction_id and status; a completed response also contains the Markdown report and any cited sources. A failed or cancelled run is returned as isError=true with a readable failure message, not as a normal structured status result.
Example transcript:
deep_research({"query": "Compare QUIC and TCP for latency-sensitive services"})
→ {"interaction_id": "abc123", "status": "in_progress"}
deep_research_result({"interaction_id": "abc123", "wait_seconds": 60})
→ {"interaction_id": "abc123", "status": "in_progress"}
deep_research_result({"interaction_id": "abc123", "wait_seconds": 60})
→ {"interaction_id": "abc123", "status": "completed", "text": "...", "sources": [...]}
The configured agents are preview-tier Gemini agents, so their names and behavior may change. The agent environment variable allows switching versions without a server release.
plan_only=true starts a collaborative-planning interaction rather than immediately producing the full report. Poll that interaction to retrieve the plan, then approve or refine it with a new deep_research call whose previous_interaction_id is the plan's id. The same field creates a follow-up that builds on a completed run. Each approval, refinement, or follow-up call creates a new interaction and returns a new id.
Deep Research is stateful at the provider because the background run must be retrieved later by id. Treat research prompts and reports according to the current retention policy of your Gemini API or Vertex AI account. This differs from google_search, which explicitly sends store=false.
Tool reference
google_search
Performs a web search using Google Search (via the Gemini API) and returns the grounded results.
Parameters:
query(string, required): The search query to find information on the web.url_context(boolean, optional): Override theGEMINI_ENABLE_URL_CONTEXTserver default for this call only.code_execution(boolean, optional): Override theGEMINI_ENABLE_CODE_EXECUTIONserver default for this call only.
Output:
query(string): The original query.text(string): The grounded response formatted as Markdown. Inline citations render as plain[n]markers whose numbers match the linked, ordered source list appended under a## Sourcesheading; each source URI appears exactly once. The whole document is normalized withmdformat(GFM).sources(array, optional): Cited sources. Each source always has a 1-basedindex;titleanduriare included only when available. The key is omitted when the answer has no usable citations.
The MCP text content is the same Markdown stored in structuredContent.text.
deep_research
Starts a billed background Deep Research run and returns immediately.
Parameters:
query(string, required): The research question.plan_only(boolean, optional): Request a collaborative research plan instead of executing it. Defaults tofalse.previous_interaction_id(string, optional): Continue, refine, or approve a prior Deep Research interaction.agent(string, optional): Override the server-configured agent for this call only withdeep-research-preview-04-2026(faster and the default) ordeep-research-max-preview-04-2026(more comprehensive). When omitted, the server-configured agent is used.
Output:
interaction_id(string): The durable identifier to pass todeep_research_result.status(string): The provider's initial interaction status, normallyin_progress.
The MCP text content also names the interaction_id, the status, and the required next tool so clients do not have to decode structuredContent before preserving the id.
deep_research_result
Fetches or briefly long-polls an existing Deep Research interaction. Reuse this tool instead of starting another run for the same question.
Parameters:
interaction_id(string, required): The identifier returned bydeep_research.wait_seconds(integer, optional): Long-poll duration from 0 through 60 seconds. Defaults to0.
Output:
interaction_id(string): The fetched interaction identifier.status(string): The current non-terminal status, orcompleted. Non-terminal provider statuses are returned as-is.text(string, completed results only): The formatted Markdown report.sources(array, optional, completed results only): Cited sources; omitted when the report has none.
For a non-terminal status, the MCP text content is a progress message. For completed, it is the full report. failed and cancelled are terminal MCP tool errors and therefore do not produce normal structured output.
Input validation and errors
All three input schemas reject missing required fields, wrong types, and unknown properties. The server validates arguments before dispatch and returns failures as ordinary MCP tool results with isError=true and readable text:
Invalid arguments for <tool>: ...for schema violationssearch query cannot be emptyorresearch query cannot be emptyfor blank queriesinteraction id cannot be emptyfor a blank Deep Research idgoogle search failed: ...ordeep research failed: ...for backend failuresdeep research cancelledordeep research <status>: <detail>for terminal research failures
Callers should inspect isError before reading structuredContent.
Development
uv sync
# Run the test suite
uv run pytest
# Format and lint
uv run ruff format --check
uv run ruff check
# Type-check
uv run ty check
# Benchmarks (deselected by default)
uv run pytest -m benchmark --benchmark-only
# Live API tests (require a real key; deselected by default)
RUN_LIVE_API=1 GEMINI_API_KEY="your-api-key" uv run pytest -m live
# Live Deep Research start/poll/cancel smoke test
RUN_LIVE_API=1 GEMINI_API_KEY="your-api-key" uv run pytest -m live
# Full multi-minute Deep Research run (explicit billed run)
RUN_SLOW=1 RUN_LIVE_API=1 GEMINI_API_KEY="your-api-key" uv run pytest -m "live and slow"
Metadata
Release files for mcp-gemini-google-search 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_gemini_google_search-0.1.0.tar.gz | 58.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_gemini_google_search-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.9 kB
Release files / mcp_gemini_google_search-0.1.0.tar.gz
| Download URL | mcp_gemini_google_search-0.1.0.tar.gz |
|---|---|
| Size | 58.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9bbf61cf73a60de1fd1eb21f57e1acfc57dceab4c3d39b189fc9e3a2ace119a5
|
|
BLAKE2b-256 checksum How to use checksums |
b5652a796c573efd69e136d2fffa05d8896c5b152df1f4b43c94c0b9f897bdee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / mcp_gemini_google_search-0.1.0-py3-none-any.whl
| Download URL | mcp_gemini_google_search-0.1.0-py3-none-any.whl |
|---|---|
| Size | 40.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
209c7a55a70043383de4aeff91d4d94ee42e6369b50e31436baab7204e3b8e6a
|
|
BLAKE2b-256 checksum How to use checksums |
106cbf9ff85b25aa7f63f8287eef937521bea0bdd5ba56e829466ecb750694ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log