mcp server
vertex-mcp-gemini
Minimal MCP gateway connecting Claude Desktop to enterprise AI providers using ADC authentication, with reproducible call recording and replay.
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 23, 2026vertex-mcp-gemini
A production-grade Model Context Protocol (MCP) server that bridges Claude and other MCP clients to Vertex AI Gemini models. It exposes text generation, image generation, multimodal reasoning, and embedding capabilities as typed MCP tools. Authentication is per-target: enterprise deployments use Application Default Credentials; preview or API-key-based models authenticate via the Gemini API. This server is infrastructure — it makes no decisions about how AI capabilities are used and enforces no output formats. It is intentionally minimal so that it can serve as a stable, provider-agnostic foundation for any MCP-driven workflow.
TL;DR
vertex-mcp-gemini is a minimal MCP capability bridge.
Claude Desktop (or any MCP client)
↓
Model Context Protocol
↓
vertex-mcp-gemini
↓
Vertex AI Gemini / Imagen / Embeddings
It exposes enterprise AI capabilities as MCP tools. Each model target in models.yaml defines its own auth mode — ADC for enterprise deployments, API key for preview or external models.
No orchestration. No prompt modification. No workflow opinion. Just capability passthrough.
Optional call recording and replay support reproducible debugging and model evaluation workflows.
Supported MCP Clients
This server is compatible with any MCP-compliant client.
Tested with:
- Claude Desktop (primary development target)
- FastMCP development tooling (
fastmcp dev)
Other MCP clients should function without modification.
Prerequisites (Claude Desktop)
Required for image generation: Filesystem connector
generate_image writes images to a shared directory on your Mac and relies on the Filesystem connector to let Claude read and display them inline. Without it, image generation will fail to display results.
Setup:
- Open Claude Desktop → Settings → Connectors
- Search for "Filesystem" and click Configure
- Add the directory you want to use as the shared image folder (default:
~/Claude)- Create the folder first if it does not exist:
mkdir ~/Claude
- Create the folder first if it does not exist:
- Click Save
Set IMAGE_OUTPUT_DIR in your .env to match the directory you configured above. They must point to the same location.
Required for image editing: source image in the shared folder
The MCP server runs on your Mac and reads image_input_path directly from the filesystem. To edit an image with Claude, copy it into your shared folder (~/Claude) first, then tell Claude the filename. Claude will construct the full path and pass it to the tool.
Quick Start (5 Minute Setup)
Requires Python 3.11 or newer.
# 1. Clone the repository
git clone https://github.com/your-org/vertex-mcp-gemini.git
cd vertex-mcp-gemini
# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
# 3. Install dependencies
pip install -r requirements.txt
# 4. Authenticate with Vertex AI (Application Default Credentials)
gcloud auth application-default login
# 5. Configure environment and model targets
cp .env.example .env
# Edit .env: set GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION (and GEMINI_API_KEY for api_key targets)
# Edit models.yaml to configure model aliases and auth modes
# 6. Run the server
python server.py
Connect Claude Desktop or any MCP client. See Setup for full configuration details.
Why This Exists
Claude and other MCP clients cannot directly call Vertex AI APIs. They communicate exclusively through the Model Context Protocol — a structured protocol over STDIO or HTTP that exposes capabilities as typed tools.
This server implements that bridge. It accepts MCP tool invocations from any compliant client, translates them into Vertex AI SDK calls via Application Default Credentials, and returns results without modification.
It exposes capabilities. It does not make decisions about how those capabilities are used.
Ecosystem Context
Claude Desktop and other MCP clients communicate exclusively through the Model Context Protocol. Enterprise Gemini deployments typically operate behind Vertex AI using Application Default Credentials.
Anthropic does not provide a first-party Vertex AI bridge. Google does not ship an MCP-native adapter for Claude Desktop.
This repository intentionally bridges that boundary.
Its responsibility is narrow: translate MCP tool invocations into enterprise-authenticated provider calls without introducing orchestration, workflow logic, or policy decisions.
Who Should Use This
This server is intended for:
- Claude Desktop users with Vertex AI access
- Developers building or experimenting with MCP workflows
- Teams that require enterprise authentication instead of API keys
- AI experimentation and reproducibility workflows
This project may not be appropriate if you are looking for:
- Agent orchestration frameworks
- Workflow automation systems
- Prompt engineering platforms
Example Use Cases
- Connecting Claude Desktop to enterprise Gemini access without API keys.
- Generating diagrams, code, or structured documents from Claude using Vertex models.
- Replaying prompts to evaluate model upgrades or compare provider outputs.
- Latency auditing and debugging production prompt workflows.
Example Scenario
A team uses Claude Desktop for research and prototyping, but production models run behind Vertex AI using enterprise authentication.
Claude cannot directly access those models.
Developers want:
- enterprise authentication instead of API keys,
- reproducible experiments,
- the ability to replay prompts when models change.
vertex-mcp-gemini sits between the MCP client and Vertex AI, exposing
enterprise models as typed tools without introducing workflow logic or
orchestration.
Claude continues to behave normally. The enterprise environment stays secure. Experiments remain reproducible.
Design Principles
-
Provider abstraction. The MCP layer calls a
BaseProviderinterface. All SDK interaction is isolated inside provider implementations underproviders/. Swapping or adding providers requires no changes outside that directory. -
STDIO transport independence. The server defaults to STDIO, which is compatible with Claude Desktop and any MCP client that spawns a subprocess. The transport layer is separated from the tool logic.
-
No workflow coupling. This server has no concept of chains, agents, memory, or orchestration. It executes a single tool call and returns.
-
No output format enforcement. Prompts and system instructions are passed to the model without modification. If a caller requests JSON, SVG, markdown, or any other structure, that is between the caller and the model.
-
Per-target authentication. Each alias in
models.yamldefines its own auth mode. ADC targets use Application Default Credentials for enterprise deployments. API key targets read a key from the env var named inapi_key_env. Auth modes are never mixed globally — each alias owns its own client. No credential values are logged by this server. -
Capability passthrough philosophy. Each tool is a thin, typed wrapper around a provider method. Business logic belongs in the caller, not here.
Architecture
server.py FastMCP instance, logging setup, entrypoint
tools.py Tool definitions — calls provider interface only
providers/
base.py BaseProvider ABC — defines the contract
vertex.py VertexProvider — all Vertex AI SDK calls (google-genai) are contained here
config.py Settings dataclass, loads models.yaml or falls back to env vars
models.yaml Model target registry — defines aliases, auth modes, and model IDs
recorder.py Optional NDJSON flight recorder
logs/calls.ndjson Written at runtime when RECORD_CALLS=1
The MCP layer never directly calls SDKs. All SDK interaction occurs inside
provider implementations. tools.py imports from providers/, never from
google.genai.
Model targets are defined in models.yaml. Each target specifies a named alias,
auth mode (adc or api_key), and model IDs. Tools accept an optional
model_alias parameter; when omitted, the default_alias from models.yaml
is used.
To add a second provider (e.g. AWS Bedrock): create providers/bedrock.py
implementing BaseProvider and add a branch in providers/__init__.py.
No other files change.
Example MCP Workflow
A user asks Claude Desktop: "Generate an SVG diagram of a three-tier web architecture."
Claude calls the generate_text tool with the prompt and a system instruction specifying SVG output format. The server delegates the invocation to VertexProvider.generate_text, which calls the Vertex AI API and returns the model's response. Claude receives the raw SVG string and presents it to the user.
The server does not inspect, modify, or validate the output. The SVG format request exists only in the prompt — the server is transparent to it. This same flow applies to any output structure: JSON schemas, code, markdown documents, or structured data.
Non Goals
- Not an agent runtime
- Not a workflow orchestrator
- Not a prompt framework
- Not opinionated about outputs
- Not responsible for rate limiting, caching, or retry logic — those belong in the caller or a gateway layer above this server
What This Server Does NOT Do
- Does not run agents or manage agent state
- Does not orchestrate multi-step workflows
- Does not maintain memory or conversation context
- Does not perform prompt engineering or modification
- Does not implement retries, caching, or rate limiting
- Does not enforce or interpret output formats
Tools Reference
Core capability tools
| Tool | Description | Returns |
|---|---|---|
generate_text | Text generation with optional system instruction and model_alias | str |
generate_image | Image generation or editing via Imagen or Gemini-native. title (2-3 words) is used as the output filename. Pass image_input_path to edit an existing image. Returns {"result": <path_or_b64>} with an optional "hint" key. Output format (PNG/JPEG) is detected from the model response and the file extension set accordingly. | dict |
save_input_image | Stage a base64-encoded image into IMAGE_OUTPUT_DIR, returning the absolute path. Use before generate_image when passing an image programmatically. | str |
get_model_options | Static generation parameter metadata for a model alias. Lists supported keys, valid ranges/enums, and notes. No API call made. | dict |
generate_multimodal | Text + optional local image input, with model_alias | str |
generate_embeddings | Vector embedding for text, with model_alias | list[float] |
call_model | Flexible escape hatch: text, vision, system instruction, format hint, and model_alias | str |
Provider introspection tools
| Tool | Description | Returns |
|---|---|---|
get_provider_info | Active provider metadata, model names, capabilities, server version | dict |
health_check | Minimal round-trip connectivity check with latency measurement | dict |
get_provider_info returns:
{
"default_alias": "enterprise_flash",
"available_targets": {
"enterprise_flash": {
"provider": "vertex",
"auth": "adc",
"text_model": "gemini-2.5-pro",
"image_model": "imagen-3.0-generate-002",
"embedding_model": "gemini-embedding-001",
"capabilities": ["embeddings", "image", "multimodal", "text"]
},
"nano_banana_preview": {
"provider": "vertex",
"auth": "api_key",
"text_model": "gemini-2.5-flash",
"image_model": "gemini-3.1-flash-image-preview",
"embedding_model": null,
"capabilities": ["image", "multimodal", "text"]
}
},
"server_version": "0.3.0"
}
health_check returns:
{
"status": "ok",
"alias": "enterprise_flash",
"latency_ms": 312.4,
"timestamp": "2026-03-02T14:00:00Z"
}
Flight recorder tools
Available at all times; return useful data only when RECORD_CALLS=1.
| Tool | Description | Returns |
|---|---|---|
recording_status | Whether recording is active and the log file path | dict |
list_calls | Most recent N recorded calls (default: 20) | list[dict] |
get_call | Single call record by request_id | dict |
replay_call | Re-execute a stored call through the provider | dict |
Image Generation Workflow
generate_image is designed to work silently with Claude Desktop's Filesystem connector (see Prerequisites).
Default behaviour (save_to_file=True)
- The server generates the image and writes it to
IMAGE_OUTPUT_DIR(default:~/Claude) using the format returned by the model (PNG or JPEG). - The tool returns a dict:
{"result": "/absolute/path/to/red-fox-snow-a3f1b2c4.png"}. The filename is derived from thetitleparameter supplied by Claude. - Claude reads the file via the Filesystem connector and displays the image inline.
- No narration, no commentary — just the image (and an optional hint line if no
generation_configwas provided).
The Filesystem connector must be enabled and configured to allow IMAGE_OUTPUT_DIR. See Prerequisites for setup steps.
Output format
The server detects the actual image format from the model response (image/png, image/jpeg, or image/webp) and saves the file with the matching extension. Do not assume .jpg — Gemini models return PNG by default.
Raw base64 (save_to_file=False)
Set save_to_file=False to receive raw base64-encoded image data in result["result"] instead of a file path. This bypasses the Filesystem connector but is only suitable for small images — Claude Desktop's tool result size limit is approximately 1 MB.
Discoverability hint
When generate_image is called without a generation_config, the response includes an additional key:
{
"result": "/Users/you/Claude/red-fox-snow-a3f1b2c4.png",
"hint": "Say 'Show me this model's options' to list supported resolutions and aspect ratios."
}
This hint is omitted when generation_config is explicitly provided.
Image editing (image_input_path)
To edit an existing image, the source file must be accessible to the MCP server — which runs on your Mac, not inside Claude's context.
Workflow:
- Copy the source image into your shared folder (e.g.
~/Claude/portrait.jpg). - Tell Claude what edit to make and where the file is:
"Edit the image at ~/Claude/portrait.jpg — change the background to a snowy mountain scene."
- Claude calls
generate_imagewith the absolute path asimage_input_pathand the edit instruction as the prompt. - The edited image is saved to
~/Claudeand displayed inline.
Do not attempt to drag an image into the Claude Desktop chat window and expect the MCP server to access it — image attachments live in Claude's sandboxed context, not on the local filesystem accessible to the server.
Gemini models receive the input image as a multimodal content part alongside the prompt.
Imagen models use the dedicated edit_image API endpoint. Optionally set edit_mode in generation_config (e.g. "inpainting-insert", "outpainting", "product-image").
All other parameters (save_to_file, generation_config, model_alias, title) work identically to standard image generation.
Error handling
If the tool call or file write fails, only the error message is returned. Claude does not narrate the attempt.
generation_config — Explicit Parameter Passthrough
generate_image accepts an optional generation_config dict. This is a pure passthrough — no server defaults are injected, no values are inferred from the prompt.
- Omitted (default): no generation parameters are sent to the API. The model uses its own native defaults entirely.
- Provided: only the keys present in the dict are forwarded. Absent keys are never sent.
Use get_model_options to discover what a given alias supports before constructing overrides.
Supported keys — Gemini image models (e.g. gemini-3.1-flash-image-preview)
| Key | Maps to | Values / Range |
|---|---|---|
aspect_ratio | ImageConfig.aspect_ratio | "1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9", "21:9" |
image_size | ImageConfig.image_size | "1K" (default), "2K", "4K" |
person_generation | ImageConfig.person_generation | "ALLOW_ALL", "ALLOW_ADULT", "ALLOW_NONE" |
thinking_level | ThinkingConfig.thinking_level | "MINIMAL" (default), "LOW", "MEDIUM", "HIGH" |
temperature | direct field | 0.0 – 2.0 |
top_p | direct field | 0.0 – 1.0 |
top_k | direct field | 1 – 40 |
seed | direct field | any integer |
All keys are passed as a flat dict. The server maps ImageConfig and ThinkingConfig keys to their correct SDK-nested structures internally.
Supported keys — Imagen models (e.g. imagen-3.0-generate-002)
| Key | Values / Range | Notes |
|---|---|---|
aspect_ratio | "1:1" (default), "3:4", "4:3", "9:16", "16:9" | |
number_of_images | 1 (default) – 4 | |
negative_prompt | any string | Describes what to avoid |
output_mime_type | "image/jpeg" (default), "image/png" | |
edit_mode | "inpainting-insert", "inpainting-remove", "outpainting", "product-image" | Only when image_input_path is provided |
guidance_scale | float | How closely output follows the prompt; only when image_input_path is provided |
seed | any integer | Fixed seed for reproducible outputs |
Example calls
Minimal — model uses its native defaults:
{"tool": "generate_image", "arguments": {"prompt": "a red fox in snow", "title": "red fox snow"}}
With explicit overrides — only these params are sent to the API:
{
"tool": "generate_image",
"arguments": {
"prompt": "a red fox in snow",
"title": "red fox snow",
"generation_config": {
"aspect_ratio": "16:9",
"image_size": "2K",
"thinking_level": "HIGH"
}
}
}
Edit an existing image — source file must already be in the shared folder:
{
"tool": "generate_image",
"arguments": {
"prompt": "change the background to a snowy mountain scene",
"title": "portrait snowy mountain",
"image_input_path": "/Users/you/Claude/portrait.jpg"
}
}
Introspect what a model alias supports:
{"tool": "get_model_options", "arguments": {"model_alias": "nano_banana_preview"}}
Flight Recorder
The flight recorder is an optional call log that records every tool invocation
to logs/calls.ndjson (one JSON object per line).
Enable with:
export RECORD_CALLS=1
By default, only a truncated prompt preview (200 chars) and a SHA-256 hash of the prompt are stored. To store complete prompts and responses:
export RECORD_FULL_PROMPTS=1
Each record includes:
{
"timestamp": "2026-03-02T14:00:00Z",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"tool": "generate_text",
"provider": "vertex",
"model": "gemini-2.5-pro",
"latency_ms": 312.4,
"prompt_hash": "a3f1b2c4d5e6f708",
"prompt_preview": "first 200 characters...",
"response_preview": "first 200 characters...",
"status": "ok"
}
Sensitive values are automatically redacted before writing. Any key matching
*_KEY, *_TOKEN, *_SECRET, or GOOGLE_APPLICATION_CREDENTIALS is replaced
with [REDACTED]. If RECORD_FULL_PROMPTS=1 is enabled, prompts and responses
are still passed through redaction rules before persistence.
Operational uses:
- Reproducibility. Replay exact calls against the same or a different model
using
replay_call. - Debugging. Correlate client-side failures to server-side call records via
request_idlogged to stderr. - Provider comparison. Replay stored calls against a future provider implementation to compare outputs.
- Latency auditing.
list_callsexposes per-call latency for all tool types.
Practical example — debugging inconsistent model output:
A model response to a specific prompt varies between runs. Enable RECORD_CALLS=1 and reproduce the issue. Use list_calls to find the relevant request_id, then get_call to inspect the exact prompt, model, and latency.
Use replay_call with the same call_id to re-run the exact invocation and confirm whether the variance is in the prompt, the model, or the call path. To investigate a provider change, update PROVIDER and replay the same stored call against the new provider to compare outputs directly.
Evaluation and experimentation workflows
Because every invocation can be recorded and replayed, the flight recorder also enables controlled experimentation workflows.
Stored calls may be replayed against newer model versions or alternate providers to compare output behavior, latency, or formatting consistency without modifying client workflows.
This allows reproducible prompt evaluation while keeping experimentation concerns outside the MCP execution path.
Setup
1. Create and activate a virtual environment
macOS / Linux:
python -m venv .venv
source .venv/bin/activate
Windows:
.venv\Scripts\activate
2. Install dependencies
pip install -r requirements.txt
3. Authenticate with Vertex AI
gcloud auth application-default login
4. Configure environment and model targets
cp .env.example .env
Edit .env — only credentials and operational settings live here:
| Variable | Required | Description |
|---|---|---|
GOOGLE_CLOUD_PROJECT | yes (ADC targets) | GCP project ID |
GOOGLE_CLOUD_LOCATION | yes (ADC targets) | Region, e.g. us-central1 |
GEMINI_API_KEY | yes (api_key targets) | Gemini API key from aistudio.google.com/apikey |
IMAGE_OUTPUT_DIR | no | Directory where generated images are saved (default: ~/Claude). Must match the directory configured in Claude Desktop under Settings → Connectors → Filesystem. |
RECORD_CALLS | no | Set to 1 to enable flight recorder |
RECORD_FULL_PROMPTS | no | Set to 1 to store full prompts/responses |
Model IDs and auth modes are configured in models.yaml, not .env. Edit models.yaml to add, remove, or change model targets:
default_alias: nano_banana_preview
targets:
enterprise_flash:
provider: vertex
auth: adc
project: ${GOOGLE_CLOUD_PROJECT}
location: ${GOOGLE_CLOUD_LOCATION}
text_model: gemini-2.5-pro
image_model: imagen-3.0-generate-002
embedding_model: gemini-embedding-001
nano_banana_preview:
provider: vertex
auth: api_key
api_key_env: GEMINI_API_KEY
text_model: gemini-2.5-flash
image_model: gemini-3.1-flash-image-preview
If models.yaml is absent, the server falls back to the legacy GEMINI_MODEL, GEMINI_IMAGE_MODEL, and GEMINI_EMBEDDING_MODEL env vars.
5. Run the server
python server.py
The server starts over STDIO and waits for an MCP client to connect. Log
output goes to stderr. Each tool invocation is logged with a request_id
for correlation.
To test interactively without a client:
fastmcp dev server.py
Troubleshooting
Claude Desktop does not detect tools
- Confirm the Python path in
claude_desktop_config.jsonis an absolute path to the virtual environment interpreter. - Restart Claude Desktop completely after any configuration change.
Authentication failures
gcloud auth application-default login
Restart the MCP server after re-authenticating.
MCP connection closes immediately
Ensure no print() or other stdout output exists in the codebase. MCP STDIO transport will break if stdout is written to by application code. All logging must go to stderr.
Running in Production Environments
STDIO isolation. This server communicates with MCP clients over stdin/stdout. Any output written to stdout by application code will corrupt the MCP protocol stream. All logging uses stderr exclusively. Do not add print() statements to tool handlers.
Logging. Structured log output goes to stderr and is safe to capture with standard process supervision tools. Each invocation emits a request_id for correlation across log lines.
Authentication. Each model target in models.yaml defines its own auth mode — adc or api_key. ADC targets use Application Default Credentials resolved by the google-genai client at startup; on GCP infrastructure the compute service account is used automatically. Locally, run gcloud auth application-default login. API key targets read the key from the env var named in api_key_env. No credential values are logged by this server.
Claude Desktop Integration
Claude Desktop launches MCP servers as subprocesses. The command path must point to the Python interpreter inside the virtual environment used to install dependencies — not the system Python.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"vertex-mcp-gemini": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/vertex-mcp-gemini/server.py"]
}
}
}
Restart Claude Desktop. The server tools appear in the tool panel.
Enable the Filesystem connector for image generation:
Go to Settings → Connectors, search for Filesystem, click Configure, and add the directory set in IMAGE_OUTPUT_DIR (default: ~/Claude). Without this, generate_image will generate the image but Claude will not be able to display it.
Extending
Adding a new tool
Add a @mcp.tool block inside register_tools() in tools.py. If it
requires a new provider capability, add an abstract method to
providers/base.py and implement it in providers/vertex.py. No other files
need to change.
Adding a new provider
- Create
providers/<name>.pyimplementingBaseProvider. - Add a branch in
providers/__init__.py. - Set
PROVIDER=<name>in your environment.
No MCP tool registration changes are required unless introducing entirely new capabilities.
Contribution Philosophy
The codebase is structured so that most changes are isolated to a single file.
Adding tools is contained to tools.py. The MCP layer is not aware of provider internals.
Adding providers is contained to a new file under providers/. The BaseProvider interface is the only contract a new implementation must satisfy. tools.py does not change.
Changing configuration is contained to config.py. New env vars belong there and nowhere else.
The surface area of a correct change is small by design. If an implementation requires edits across more than two files, the abstraction boundaries should be reviewed before proceeding.
Versioning Philosophy
Minor versions may introduce new tools or extend provider capabilities without breaking existing MCP client integrations.
Major versions indicate changes to the MCP interface contract or the BaseProvider interface — changes that may require updates in clients or provider implementations.
The goal is long-term stability for MCP clients. New capabilities are additive.
Operational Notes
- The
logs/directory is created automatically when recording is enabled. calls.ndjsonis append-only and safe for incremental inspection.
Security Posture
- ADC targets use Application Default Credentials; no credential values are stored or logged by this server.
- API key targets read the key from the env var named in
api_key_env; the value is never logged. - Sensitive values are redacted before persistence in the flight recorder.
- Flight recording is disabled by default.
Long-Term Possibilities (Optional)
This project intentionally focuses on a narrow responsibility: provide a clean MCP capability bridge between clients and enterprise AI providers.
The architecture allows expansion if operational requirements change. Potential directions include:
- Multi-provider routing. Support Anthropic, OpenAI, Bedrock, or local models behind the same MCP interface without changing client workflows.
- Provider evaluation workflows. Use flight recorder replay to benchmark prompts or compare outputs across models.
- Operational observability. Expand introspection and recording into lightweight tracing or latency auditing.
- Policy or governance middleware. Optional safety rules, cost controls, or organizational guardrails before provider execution.
- Experimentation control plane. Coordinate reproducible AI experimentation across environments or research workflows.
Possible future expansions are intentionally opt-in layers above the MCP bridge rather than additions to the core server. The core goal is long-term simplicity.
These remain outside the current implementation scope. The repository stays minimal unless a clear operational need emerges.
License
MIT License. Use freely. Attribution appreciated but not required.
Topics
MCP · Claude Desktop · Vertex AI · Gemini · Imagen · Application Default Credentials · provider abstraction · AI infrastructure gateway · FastMCP · python · google-genai