Skip to content
Conduit

mcp server

vertex-mcp-gemini

Minimal MCP gateway connecting Claude Desktop to enterprise AI providers using ADC authentication, with reproducible call recording and replay.

michaelandrewgamble/vertex-mcp-geminicommunitylistedunreviewed

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, 2026

vertex-mcp-gemini

Build Status Python License

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:

  1. Open Claude Desktop → Settings → Connectors
  2. Search for "Filesystem" and click Configure
  3. 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
  4. 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 BaseProvider interface. All SDK interaction is isolated inside provider implementations under providers/. 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.yaml defines its own auth mode. ADC targets use Application Default Credentials for enterprise deployments. API key targets read a key from the env var named in api_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

ToolDescriptionReturns
generate_textText generation with optional system instruction and model_aliasstr
generate_imageImage 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_imageStage a base64-encoded image into IMAGE_OUTPUT_DIR, returning the absolute path. Use before generate_image when passing an image programmatically.str
get_model_optionsStatic generation parameter metadata for a model alias. Lists supported keys, valid ranges/enums, and notes. No API call made.dict
generate_multimodalText + optional local image input, with model_aliasstr
generate_embeddingsVector embedding for text, with model_aliaslist[float]
call_modelFlexible escape hatch: text, vision, system instruction, format hint, and model_aliasstr

Provider introspection tools

ToolDescriptionReturns
get_provider_infoActive provider metadata, model names, capabilities, server versiondict
health_checkMinimal round-trip connectivity check with latency measurementdict

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.

ToolDescriptionReturns
recording_statusWhether recording is active and the log file pathdict
list_callsMost recent N recorded calls (default: 20)list[dict]
get_callSingle call record by request_iddict
replay_callRe-execute a stored call through the providerdict

Image Generation Workflow

generate_image is designed to work silently with Claude Desktop's Filesystem connector (see Prerequisites).

Default behaviour (save_to_file=True)

  1. The server generates the image and writes it to IMAGE_OUTPUT_DIR (default: ~/Claude) using the format returned by the model (PNG or JPEG).
  2. The tool returns a dict: {"result": "/absolute/path/to/red-fox-snow-a3f1b2c4.png"}. The filename is derived from the title parameter supplied by Claude.
  3. Claude reads the file via the Filesystem connector and displays the image inline.
  4. No narration, no commentary — just the image (and an optional hint line if no generation_config was 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:

  1. Copy the source image into your shared folder (e.g. ~/Claude/portrait.jpg).
  2. 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."

  3. Claude calls generate_image with the absolute path as image_input_path and the edit instruction as the prompt.
  4. The edited image is saved to ~/Claude and 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)

KeyMaps toValues / Range
aspect_ratioImageConfig.aspect_ratio"1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9", "21:9"
image_sizeImageConfig.image_size"1K" (default), "2K", "4K"
person_generationImageConfig.person_generation"ALLOW_ALL", "ALLOW_ADULT", "ALLOW_NONE"
thinking_levelThinkingConfig.thinking_level"MINIMAL" (default), "LOW", "MEDIUM", "HIGH"
temperaturedirect field0.0 – 2.0
top_pdirect field0.0 – 1.0
top_kdirect field1 – 40
seeddirect fieldany 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)

KeyValues / RangeNotes
aspect_ratio"1:1" (default), "3:4", "4:3", "9:16", "16:9"
number_of_images1 (default) – 4
negative_promptany stringDescribes 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_scalefloatHow closely output follows the prompt; only when image_input_path is provided
seedany integerFixed 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_id logged to stderr.
  • Provider comparison. Replay stored calls against a future provider implementation to compare outputs.
  • Latency auditing. list_calls exposes 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:

VariableRequiredDescription
GOOGLE_CLOUD_PROJECTyes (ADC targets)GCP project ID
GOOGLE_CLOUD_LOCATIONyes (ADC targets)Region, e.g. us-central1
GEMINI_API_KEYyes (api_key targets)Gemini API key from aistudio.google.com/apikey
IMAGE_OUTPUT_DIRnoDirectory where generated images are saved (default: ~/Claude). Must match the directory configured in Claude Desktop under Settings → Connectors → Filesystem.
RECORD_CALLSnoSet to 1 to enable flight recorder
RECORD_FULL_PROMPTSnoSet 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.json is 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

  1. Create providers/<name>.py implementing BaseProvider.
  2. Add a branch in providers/__init__.py.
  3. 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.ndjson is 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

↑↓ navigate openesc close