Skip to main content
Glama
machinemates-ai

Gemini Research MCP Server

README.md
# Gemini Research MCP Server

[![PyPI version](https://img.shields.io/pypi/v/gemini-research-mcp.svg)](https://pypi.org/project/gemini-research-mcp/)
[![CI](https://github.com/machinemates-ai/gemini-research-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/machinemates-ai/gemini-research-mcp/actions/workflows/ci.yml)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

MCP server for AI-powered research using **Gemini**. Fast grounded search, URL extraction, comprehensive Deep Research, and session management.

Built on **FastMCP `4.1`** with the modern sessionless
MCP protocol, **Gemini `4 Argon`** for web research and follow-ups
(`3.8 Flash` for helpers), MCP Tasks (SEP-1732), the guard-pattern
elicitation flow, a BM25-compacted tool catalog, and pluggable Disk/Redis
storage with a zero-configuration local default.

## Architecture

![Architecture](https://raw.githubusercontent.com/machinemates-ai/gemini-research-mcp/main/docs/architecture.png)

<details>
<summary>Mermaid source</summary>

```mermaid
flowchart TB
    subgraph Client["MCP Client"]
        Claude["Claude / Copilot"]
    end

    subgraph Server["gemini-research-mcp"]
        direction TB
        FastMCP["FastMCP 4 Server<br/>@mcp.tool()<br/>BM25SearchTransform"]
        
        subgraph Tools["Tools"]
            RW["research_web<br/>Grounded lookup"]
            RD["research_deep<br/>Autonomous 3-20min"]
            RF["research_followup<br/>Continue session"]
            RR["resume_research<br/>Recover interrupted"]
            FW["fetch_webpage<br/>Content extraction"]
            EX["export_research_session<br/>MD/JSON/DOCX"]
            LS["list_research_sessions"]
            LT["list_format_templates"]
        end

        subgraph Modules["Core Modules"]
            Quick["quick.py<br/>Web grounding"]
            Deep["deep.py<br/>Deep research agent"]
            Content["content.py<br/>SSRF protection"]
            StorageMod["storage.py<br/>Session + artifact store"]
            Templates["templates.py<br/>Format templates"]
        end
    end

    subgraph External["External Services"]
        Gemini["Google Gemini API"]
        Web["Web Sources<br/>via trafilatura"]
    end

    subgraph Storage["Persistence"]
        Disk["DiskStore<br/>XDG data directory"]
        Redis["Redis/Valkey<br/>shared multi-worker storage"]
    end

    Claude -->|"MCP Protocol"| FastMCP
    FastMCP --> Tools
    
    RW --> Quick
    RD --> Deep
    RF --> StorageMod
    RR --> StorageMod
    FW --> Content
    LT --> Templates
    
    Quick -->|"grounding"| Gemini
    Deep -->|"agentic"| Gemini
    Content -->|"httpx"| Web
    StorageMod --> Disk
    StorageMod -.-> Redis
```

</details>

## Tools

The server exposes a **BM25-compacted catalog**: only the 7 tools below plus
the synthetic `search_tools`/`call_tool` pair are listed by default
(`fastmcp.server.transforms.search.BM25SearchTransform`). Utility tools
(`fetch_webpage`, `research_followup`, `list_research_sessions`,
`list_format_templates`, `refine_research_plan`,
`inspect_mcp_server_for_gemini`, `list_research_engines`,
`list_research_runs`, `get_research_run`, `export_research_bundle`) are hidden from the default
listing to keep the catalog small for LLM tool-selection, but remain fully
callable directly by name or via the `call_tool` proxy, and are discoverable
by relevance through `search_tools`.

| Tool | Description | Latency | Visible by default |
|------|-------------|---------|---------------------|
| `research_web` | Web search with citations | varies | ✅ |
| `research_deep` | Multi-step autonomous research (MCP Tasks) | 3-20 min | ✅ |
| `research_deep_max` | Maximum-comprehensiveness Deep Research for exhaustive/high-stakes work | longer-running | ✅ |
| `research_run` | Engine-neutral research job (MCP Tasks): Gemini by default, optional local engine | engine-dependent | ✅ |
| `research_compare` | Run exactly two engines on one query and return attributed reports (no synthesis) | engine-dependent | ✅ |
| `resume_research` | Resume interrupted/in-progress sessions | instant | ✅ |
| `export_research_session` | Disk-first export to persistent Markdown, JSON, or DOCX artifacts | instant | ✅ |
| `search_tools` | Discover hidden utility tools by relevance (BM25) | instant | ✅ |
| `call_tool` | Proxy to invoke any hidden tool by name | varies | ✅ |
| `research_followup` | Continue conversation after research | varies | discoverable |
| `list_research_sessions` | List saved research sessions | instant | discoverable |
| `list_format_templates` | Browse report format templates | instant | discoverable |
| `refine_research_plan` | Iterate on or approve a `collaborative_planning=True` plan | instant-3min | discoverable |
| `fetch_webpage` | Extract article content from a specific URL (SSRF-protected, chunkable) | 0.5-2 sec | discoverable |
| `inspect_mcp_server_for_gemini` | Inspect remote MCP reachability and schemas (diagnostic only) | varies | discoverable |
| `list_research_engines` | Engines, backends, models and availability (configuration only, no provider call) | instant | discoverable |
| `list_research_runs` | Metadata of your research jobs and comparisons (`limit` 1-100) | instant | discoverable |
| `get_research_run` | Read one run by `run_id`; `refresh=true` re-polls a native provider job | instant | discoverable |
| `export_research_bundle` | Attributed JSON/Markdown bundle of a run, with no model or search call | instant | discoverable |

### `research_deep` / `research_deep_max` Deep Research parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|--------------|
| `visualization` | `"off"` \| `"auto"` | `"off"` | Let the agent produce and persist supporting images/charts. Images are persisted as MCP resource artifacts (`research://exports/{id}`), never inlined as text. |
| `collaborative_planning` | boolean | `false` | Return the drafted research plan and an interaction ID instead of running the full report. Approve or iterate on the plan with `refine_research_plan(previous_interaction_id=..., decision="approve"|"iterate")`. |
| `mcp_servers` | array \| null | `null` | **Disabled.** Any non-empty value fails before network or Gemini API access because provider-side Deep Research remote MCP is not reliable. |

### `fetch_webpage` Parameters

`fetch_webpage` is discoverable through `search_tools` in the default server listing.

The `fetch_webpage` tool supports chunked reading for large pages and optional proxy routing:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | string | required | HTTP/HTTPS URL to fetch |
| `max_length` | integer \| null | `null` | Maximum characters to return (chunk size) |
| `start_index` | integer | `0` | Character offset for pagination |
| `proxy_url` | string \| null | `null` | Optional HTTP(S) proxy URL for the request |

Notes:
- SSRF protection is always applied (private/internal hosts are blocked).
- `robots.txt` is checked before fetch when `protego` is installed.
- When output is truncated, the response includes a continuation hint with next `start_index`.
- If `proxy_url` is omitted, the server falls back to `FETCH_PROXY_URL` when set.
- `proxy_url` must be a public HTTP(S) host (private/internal proxy hosts are blocked).

Install the `web` extra for the highest-quality `fetch_webpage` experience:

```bash
pip install 'gemini-research-mcp[web]'
# or
uv add 'gemini-research-mcp[web]'
```

Without `[web]`, `fetch_webpage` still works using the built-in HTML fallback, but `trafilatura`
extraction and `protego`-based `robots.txt` checks are unavailable.

### Power User Workflow

[![Power User Workflow](https://raw.githubusercontent.com/MachineMates-AI/gemini-research-mcp/main/docs/workflow.svg)](docs/workflow.svg)

> **Key insight**: Gemini Deep Research runs asynchronously on Google's servers. Even if VS Code disconnects, your research continues. The `resume_research` tool retrieves completed work.

### Features

- **Auto-Clarification**: `research_deep` asks clarifying questions for vague queries. On the modern sessionless MCP protocol this uses a stateless guard pattern (`InputRequiredResult`, two independent tool calls, no server-held connection); legacy handshake clients still use [MCP Elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation) (`ctx.elicit()`)
- **Deep Research Max**: `research_deep_max` exposes Google's Max agent for exhaustive, high-stakes, and offline research workflows
- **Collaborative Planning**: `research_deep(..., collaborative_planning=True)` returns the drafted plan for approval before running the full report; refine or approve it with `refine_research_plan`
- **Visualization**: `visualization="auto"` lets Deep Research produce supporting images, persisted as downloadable MCP resource artifacts
- **MCP Tasks**: [Real-time progress](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) with streaming updates
- **Session Persistence**: Research sessions are automatically saved and can be resumed later; shareable across instances with Redis (see [Storage backends](#storage-backends))
- **Persistent, Disk-First Exports**: Export to Markdown, JSON, or professional DOCX with Table of Contents; artifacts survive restarts and can be shared through Redis while files are still written to disk by default
- **File Search**: Search your own data alongside web using `file_search_store_names`
- **Fail-closed remote MCP**: Deep Research rejects `mcp_servers` before network/provider access until Google exposes a reliable structured result contract
- **Format Instructions**: Control report structure (sections, tables, tone)
- **LangChain-ready**: verified consumable via `langchain.mcp.MCPAdapter` (LangChain `1.4.0a2`) over both stdio and streamable-http - see [`scripts/langchain_interop_smoke.py`](scripts/langchain_interop_smoke.py). LangChain is never a dependency of this package.

## Installation

### PyPI (recommended)

```bash
pip install gemini-research-mcp
# or
uv add gemini-research-mcp
```

### Claude Desktop (MCPB Bundle)

Download the `.mcpb` bundle from [GitHub Releases](https://github.com/machinemates-ai/gemini-research-mcp/releases) and open it in Claude Desktop for single-click installation.

The bundle uses UV runtime - dependencies are installed automatically, no Python required.

## Research jobs and engines

`research_run` and `research_compare` are **engine-neutral** jobs. They require
MCP Tasks, return a normalized result envelope
(`{"result": <text>, "schema_version": 1, "run": {...}}`), and keep the legacy
`research_deep`/`resume_research`/`export_research_session` tools working.

| Engine | Backend | Notes |
|--------|---------|-------|
| `gemini` (default) | `gemini-native` | Gemini Deep Research; recoverable by provider interaction ID |
| `gemini_max` | `gemini-native` | Gemini Deep Research Max |
| `local_deep_research` | `local-deep-research` | Optional, operator-installed [Local Deep Research](https://github.com/LearningCircuit/local-deep-research) bridge; see below |

`research_compare` takes **exactly two distinct** engines and runs both. It
returns each engine's report, attributed and verbatim; it never merges or
synthesizes them. **Synthesis is the caller's job.** One engine failing yields a
`partial` result that keeps the other report. Comparing two Gemini profiles can
incur two paid research workloads.

### Identifiers

Three IDs are never interchangeable:

- **Task ID**: the MCP Task handle for one submission. A resubmitted Task is a new
  submission and **may create a second paid job** (the same logical request is
  not deduplicated across Tasks; there are no caller idempotency keys).
- **Run ID** (`job_…`, `cmp_…`): stable, derived from the owner and Task. Use it
  with `get_research_run`, `export_research_bundle` and `list_research_runs`.
- **Provider ID**: the Gemini interaction ID. Only native jobs have one; local
  jobs never expose a provider ID. Provider IDs are not accepted as run IDs.

### Lifecycle and recovery

- Job statuses: `queued`, `running`, `completed`, `failed`, `cancelled`,
  `interrupted`, `expired`; a comparison can also be `partial`. `interrupted`
  means the server cannot prove an outcome.
- Retention uses the existing fixed TTL (`GEMINI_RESEARCH_TTL_SECONDS`) set at
  creation; updates and refreshes never extend it. Expired runs read `expired`.
  A submission is refused (`RETENTION_TOO_SHORT`) when the TTL cannot cover the
  engine timeout plus a cleanup margin.
- **Ambiguous submission**: if the process dies after the provider request may
  have been sent but before its ID was stored, the run reads `interrupted` with
  `SUBMISSION_UNKNOWN`. It is not resubmitted automatically; the provider job may
  still exist and be billed.
- **Native recovery**: a native run with a stored provider ID is recovered by
  `get_research_run(refresh=true)` or the legacy `resume_research`. While this
  process still executes the job, `resume_research` answers `busy`.
- **Local non-recovery**: a local run is blocking and has no provider ID. After a
  restart it is reported `interrupted`; it is never resumed or re-run.
- **Single owner**: submission requires one owner process with in-memory Tasks
  and local storage. With `GEMINI_RESEARCH_STORAGE_URL` (Redis) or a non-memory
  `FASTMCP_DOCKET_URL`, submissions are refused with
  `DISTRIBUTED_SUBMISSION_UNSUPPORTED` rather than risk duplicate paid jobs after
  a redelivery or a new runner. Read tools still work.

### Ownership and HTTP

Runs, their linked sessions, images and bundles are owned by the authenticated
principal. Another principal sees "not found". Over `streamable-http` the job
tools require an authenticated principal (`AUTH_REQUIRED` otherwise); stdio has a
single local owner. Historical sessions with no job link stay visible as before.
The local engine is **not** offered over HTTP unless the operator sets
`GEMINI_RESEARCH_LDR_ALLOW_HTTP=true`
(`LOCAL_ENGINE_HTTP_DISABLED` otherwise).

### Follow-ups on agent research

The Gemini API does not let a model continue an interaction that belongs to a
Deep Research agent. `research_followup` therefore answers from the report stored
in the session, treated as untrusted context, in a stateless model call.

### Optional local engine (Local Deep Research)

The bridge targets an operator-installed `ldr-mcp` pinned to LDR commit
`362d65652156732b08a704f0d3faa77996b4f0de` (stdio only). This server **installs
and starts nothing**: set `GEMINI_RESEARCH_LDR_COMMAND` to its absolute path and
configure the `LDR_*` values in [`.env.example`](.env.example) (Ollama on
loopback plus an explicit search backend). The engine stays unavailable until
all are set, and the Gemini-only default never spawns a child process.

- **Local does not mean free or private by default**: the model runs on your
  Ollama, but the configured search backend receives queries over the network,
  and local compute costs time and hardware.
- LDR keeps its own data and retention, separate from this server's TTL. The
  child process is not sandboxed. LDR's configuration is verified before any
  query is sent.
- No boolean here is human authorization; the caller decides whether to run a
  paid or networked workload.
- LDR can return a report with **no sources** when its search backend throttles
  or its relevance filter drops every result (observed with Wikipedia and a 4B
  model). The bundle then lists no sources for that engine; the report is still
  kept verbatim and untrusted.

### Bundles

`export_research_bundle(run_id, format="json"|"markdown")` renders a run (single
or comparison) from stored data with **no model, search or provider call**. Each
engine entry carries engine, backend, model, status, recovery state, warnings,
error, sources, usage and its report, labelled verbatim and untrusted. Bundles
are saved to the export directory and as an owner-scoped `research://exports/{id}`
artifact.

## Configuration

| Variable | Required | Default | Description |
|----------|----------|---------|--------------|
| `GEMINI_API_KEY` | **Yes** | — | [Google AI Studio API key](https://aistudio.google.com/apikey) |
| `GEMINI_MODEL` | No | `gemini-4-argon` | Shared model for `research_web` and `research_followup` |
| `GEMINI_SUMMARY_MODEL` | No | `gemini-3.8-flash` | Model for session metadata, titles, and semantic session matching (thinking level `low`) |
| `DEEP_RESEARCH_AGENT` | No | `deep-research-preview-04-2026` | Default agent for `research_deep`; accepts `fast`, `standard`, `deep-research`, `max`, `deep-research-max`, or exact agent IDs |
| `FETCH_PROXY_URL` | No | — | Default HTTP(S) proxy for `fetch_webpage` |
| `GEMINI_RESEARCH_STORAGE_URL` | No | — | `redis://...` URL to share sessions, exports, and (unless overridden) Tasks across multiple instances/workers. Local DiskStore + memory Tasks remain the default |
| `FASTMCP_DOCKET_URL` | No | — | Advanced Tasks backend override. Takes priority over `GEMINI_RESEARCH_STORAGE_URL`; unset preserves memory Tasks locally |
| `GEMINI_RESEARCH_STORAGE_PATH` | No | XDG data dir | Custom directory for the local `DiskStore` |
| `GEMINI_RESEARCH_TTL_SECONDS` | No | backend default | Override session/export TTL |
| `GEMINI_RESEARCH_EXPORT_DIR` | No | `~/.gemini-research/exports/` | Disk-first destination when `export_research_session` has no `output_path` |
| `GEMINI_RESEARCH_LDR_COMMAND` | No | — | Absolute path of an operator-installed `ldr-mcp`; enables the optional `local_deep_research` engine (see [Research jobs and engines](#research-jobs-and-engines)) |
| `GEMINI_RESEARCH_LDR_ALLOW_HTTP` | No | `false` | Operator opt-in to offer the local engine over `streamable-http` |
| `GEMINI_RESEARCH_TRANSPORT` | No | `stdio` | `stdio` (default, historical) or `streamable-http` (opt-in, see [Transports](#transports)) |
| `GEMINI_RESEARCH_HTTP_HOST` | No | `127.0.0.1` | Bind host for `streamable-http`. Non-loopback requires `GEMINI_RESEARCH_HTTP_BEARER_TOKEN` |
| `GEMINI_RESEARCH_HTTP_PORT` | No | `8000` | Bind port for `streamable-http` |
| `GEMINI_RESEARCH_HTTP_PATH` | No | `/mcp` | URL path for `streamable-http` |
| `GEMINI_RESEARCH_HTTP_BEARER_TOKEN` | No | — | Static bearer token required to call `streamable-http`. Never reuse `GEMINI_API_KEY` for this |

The default [Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon)
model requires [paid-tier API access](https://ai.google.dev/gemini-api/docs/pricing#gemini-4-argon).
To use the previous Flash model for web research and follow-ups, explicitly set
`GEMINI_MODEL=gemini-3.8-flash` in the server environment. The server does not
silently fall back to another model.

Query clarification uses fixed `gemini-3.8-flash`; it is not controlled by
`GEMINI_SUMMARY_MODEL`. Web research preserves HIGH thinking. Latency varies
with the query and provider load; neither web research nor follow-ups have a
fixed completion-time guarantee.

```bash
cp .env.example .env
# Edit .env with your API key
```

## Transports

The server defaults to **stdio**, matching every existing VS Code/Claude
Desktop configuration - no changes required for local, single-client use.

**Streamable HTTP** is opt-in, for remote or multi-client/multi-worker
deployments, and is sessionless (no sticky session required across calls):

```bash
# Local-only (no auth required, loopback binding):
uv run gemini-research-mcp --transport streamable-http

# Remote-accessible (bearer token required - refuses to start otherwise):
GEMINI_RESEARCH_HTTP_BEARER_TOKEN=$(openssl rand -hex 32) \
  uv run gemini-research-mcp --transport streamable-http --host 0.0.0.0 --port 8000
```

Binding to any non-loopback host (`0.0.0.0`, `::`, a LAN/public IP, etc.)
without `GEMINI_RESEARCH_HTTP_BEARER_TOKEN` set causes the server to refuse to
start - this prevents accidentally exposing your Gemini API quota to the
public internet. `127.0.0.1`/`localhost`/`::1` never require a token.

`--transport`, `--host`, `--port`, and `--path` CLI flags mirror the
`GEMINI_RESEARCH_TRANSPORT`/`GEMINI_RESEARCH_HTTP_HOST`/`GEMINI_RESEARCH_HTTP_PORT`/`GEMINI_RESEARCH_HTTP_PATH`
environment variables (CLI flags take precedence).

## Storage backends

Research sessions and export artifacts are stored through a single
backend-agnostic layer:

- **Local (default, no Redis required)**: sessions and exports use `DiskStore`
  under the XDG data directory, while FastMCP Tasks use in-process memory
  (`GEMINI_RESEARCH_STORAGE_PATH` to override) - zero configuration, single
  process/single machine.
- **Distributed (Redis/Valkey)**: set `GEMINI_RESEARCH_STORAGE_URL=redis://host:6379/0`
  to share sessions, exports, and Tasks across multiple server instances or
  workers. Set `FASTMCP_DOCKET_URL` only when Tasks must use a different
  backend. Requires the `distributed` extra:

```bash
uv add 'gemini-research-mcp[distributed]'
```



### Deep Research vs Deep Research Max

Google exposes Deep Research variants through the Gemini Interactions API `agent`
field, not the regular Gemini `model` field:

- `research_deep` uses `deep-research-preview-04-2026` by default. Use it for
  interactive research, comparisons, investigations, and latency/cost-sensitive
  synthesis.
- `research_deep_max` uses `deep-research-max-preview-04-2026`. Use it when the
  user explicitly asks for Max, exhaustive/comprehensive due diligence, market
  maps, literature reviews, board-ready reports, offline/nightly research, or
  maximum completeness over speed.

For Copilot and other LLM clients, the two tools are intentionally separate so
Max can be selected from the tool name and description. There is no public
`model` parameter for Deep Research, because follow-up and quick research use
Gemini models while Deep Research uses Interactions agents.

### Remote MCP servers for Deep Research

**Disabled since `v0.16.0b2`.** Any non-empty `mcp_servers` value is rejected for
both `research_deep` and `research_deep_max` before remote inspection, network
access, or Gemini API consumption.

The request shape is valid and Google documents MCP as a Deep Research tool,
but repeated paid E2E runs completed without any
`mcp_server_tool_call`/`mcp_server_tool_result` steps. One run also invented
substitute evidence after failing to obtain the fixture data. See
[`googleapis/python-genai#2126`](https://github.com/googleapis/python-genai/issues/2126).

`inspect_mcp_server_for_gemini` remains available to inspect endpoint
reachability, tool names, and schema compatibility. It does **not** enable the
disabled Deep Research integration.

Remote MCP will only be reconsidered after an upstream correction and repeated
E2E runs that retain non-empty structured tool-call and tool-result steps.


## Usage

### VS Code MCP

Add to `.vscode/mcp.json`:

```json
{
  "servers": {
    "gemini-research": {
      "command": "uvx",
      "args": ["gemini-research-mcp"],
      "env": {
        "GEMINI_API_KEY": "your-api-key"
      }
    }
  }
}
```

Or run from source:

```json
{
  "servers": {
    "gemini-research": {
      "command": "uv",
      "args": ["run", "--directory", "path/to/gemini-research-mcp", "gemini-research-mcp"],
      "envFile": "${workspaceFolder}/path/to/gemini-research-mcp/.env"
    }
  }
}
```

### Command Line

```bash
uv run gemini-research-mcp
# or
uvx gemini-research-mcp
```

## DOCX Export

Export research sessions to professional Word documents with:

- **Cover page** with title, date, and research metadata
- **Clickable Table of Contents** with navigation to sections
- **Professional typography**: Calibri fonts, 1-inch margins, 1.5x line spacing
- **Executive summary** with elegant formatting
- **Full research report** with proper heading hierarchy
- **Sources section** with full clickable URLs
- **Metadata table** with session details

### VS Code Setup

To enable DOCX export, install with the `[docx]` extra:

```json
{
  "servers": {
    "gemini-research": {
      "command": "uvx",
      "args": ["--from", "gemini-research-mcp[docx]", "gemini-research-mcp"],
      "env": {
        "GEMINI_API_KEY": "your-api-key"
      }
    }
  }
}
```

### Downloading Files

`export_research_session` is **disk-first**: the file is always written
to disk and the absolute path is returned on the first line of the
response text (e.g. `✅ **Saved to:** /…/report.docx`). This means any
MCP client — GUI or headless — gets a usable file path back.

By default exports are written to `GEMINI_RESEARCH_EXPORT_DIR`
(defaults to `~/.gemini-research/exports/`; falls back to the system
temp dir if that location isn't writable). Override per-call with the
`output_path` argument:

```jsonc
{
  "name": "export_research_session",
  "arguments": {
    "interaction_id": "v1_...",
    "format": "docx",
    "output_path": "/absolute/or/relative/path/report.docx"
  }
}
```

When `output_path` is supplied, the parent directory must already
exist (no silent `mkdir`). GUI hosts (e.g. VS Code Copilot Chat) also
receive an `EmbeddedResource` attachment backed by the persistent
`research://exports/{id}` resource store for native "Save As" — clients that can't render it can safely ignore it.

### Client compatibility

`research_deep` requires [MCP Tasks support](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks)
(SEP-1732) on the client. Clients that do not advertise the `tasks`
capability will receive a `-32600` error.

Known client status:

- **VS Code Copilot Chat / MCP Inspector / Claude Desktop** — supported.
- **GitHub Copilot CLI** — tracked upstream at
  [github/copilot-cli#2538](https://github.com/github/copilot-cli/issues/2538);
  until that lands, use `research_web` from the CLI.

### Installation (pip/uv)

```bash
# Install with DOCX support
pip install 'gemini-research-mcp[docx]'
# or
uv add 'gemini-research-mcp[docx]'
```

### Features

| Feature | Description |
|---------|-------------|
| **Cover Page** | Title, date, duration, tokens, AI agent |
| **Clickable TOC** | Internal hyperlinks navigate to sections |
| **Syntax Highlighting** | Pygments-powered code blocks with GitHub colors |
| **Professional Styling** | Calibri fonts, proper heading hierarchy (H1-H4) |
| **Page Margins** | Standard 1-inch (2.54cm) margins |
| **Heading Spacing** | `keep_with_next` prevents orphan headings |
| **Sources** | Full URLs as clickable hyperlinks |
| **Pure Python** | No external binaries (Pandoc not required) |

## Resources

MCP Resources provide read-only data that clients can access:

| Resource | Description |
|----------|-------------|
| `research://models` | Available models and their capabilities |
| `research://exports` | List cached exports ready for download |
| `research://exports/{id}` | Download an exported file (Markdown, JSON, or DOCX) |

### File Downloads

The `export_research_session` tool creates exports and returns a resource URI. Clients (like VS Code) can then fetch the resource to download the file with proper MIME type handling.

## Development

```bash
uv sync --extra dev
uv run pytest
uv run mypy src/
uv run ruff check src/
```

Release numbering and gates are described in [`docs/versioning.md`](docs/versioning.md).

### Tests

```bash
uv run pytest                    # Unit tests
uv run pytest -m e2e             # E2E tests (requires GEMINI_API_KEY)
uv run pytest --cov=src/gemini_research_mcp  # With coverage
```

## Pricing

| Tool | Typical Cost |
|------|-------------|
| `research_web` | ~$0.01-0.05 per query |
| `research_deep` | ~$2-5 per task |

*Deep Research uses ~80-160 searches and ~250k-900k tokens per task.*

## License

MIT

TDQS

A3.7/5.0

Scored across 9 tools

Disambiguation3/5

The research tools are mostly distinguishable by depth, latency, and explicit use-case guidance, but research_run, research_deep, research_deep_max, and research_web have nuanced overlapping intent. The generic search_tools/call_tool pair is clear as meta-tools but sits outside the research workflow.

Naming Consistency4/5

All names use snake_case and are readable, with a clear research_* family and verb_noun-style lifecycle/meta tools. The convention is not perfectly uniform because some tools use noun-prefixed research_* names while others start with a verb, but this is a minor deviation.

Tool Count5/5

Nine tools is well-scoped for a research server with quick web lookup, deep research variants, comparison, resume/export lifecycle, and meta-tool discovery. Each tool appears to serve a distinct workflow rather than inflating the surface unnecessarily.

Completeness3/5

The surface covers research web/deep/max/compare, resume, and export, but research_run explicitly relies on get_research_run, which is not present, leaving background research jobs with no stated way to inspect or recover results. Similarly, resume_research references list_research_sessions, which is absent from the tool set.

Maintenance

ActivityMaintained
ResponsivenessNo issues