⌂ Home
Gemini CLI: From Zero to Production
Track 4 — Integrations
Module 11 of 17 ~45 min Intermediate
Track 4
Welcome to Track 4 — Integrations. This track covers connecting Gemini CLI to external services, APIs, and data sources via the Model Context Protocol and first-party Google extensions.
Module 10 · Track 4 — Integrations

MCP Servers & Extensions

Out of the box, Gemini CLI can read files, search the web, and run shell commands. MCP servers extend that into a limitless integration surface: your databases, your APIs, your SaaS tools. This module covers the MCP protocol, how to install community and Google partner extensions, how the Google Workspace extension gives Gemini 100+ built-in Gmail/Docs/Drive skills, and how to build your own MCP server in 20 lines.

What You'll Learn

  • Understand the three MCP primitives — Tool, Resource, Prompt — and how Gemini CLI uses each
  • Install community MCP servers and configure them via GEMINI.md
  • Survey the Google + partner extension catalog: Cloud Run, BigQuery, Figma, Stripe, and more
  • Use the Google Workspace extension for Gmail, Docs, Drive, Sheets, and Calendar automation
  • Configure OAuth 2.0 for remote MCP servers and prevent credential leakage via env var sanitization
  • Build a minimal custom MCP server with FastMCP and connect it to Gemini CLI in under 30 minutes

What is MCP?

Analogy — USB-C for AI Tools

Before USB-C, connecting a peripheral to a laptop required knowing whether it used USB-A, Micro-B, Thunderbolt 2, or DisplayPort — each device manufacturer had its own plug. USB-C created one standard connector. MCP does the same for AI models and external tools: instead of every LLM vendor building bespoke integrations with every SaaS product, MCP defines one protocol that any model can speak and any service can implement.

The pain without MCP: Claude needs a Claude-specific Slack connector; Gemini needs a Gemini-specific Slack connector; the LLM vendor ships integrations, not the tool vendor. The mapping: Slack ships one mcp-slack-server — any MCP-compatible model (Gemini, Claude, GPT) connects to it identically. One server, all models.

Technical Definition — Model Context Protocol

The Model Context Protocol (MCP)An open standard developed by Anthropic (published November 2024) that defines how AI models communicate with external tools and data sources. An MCP server exposes capabilities as Tools, Resources, and Prompts. The model client (e.g., Gemini CLI) discovers capabilities, calls them during inference, and incorporates results into its response. Google adopted MCP natively in Gemini CLI 0.1.x. is a JSON-RPC 2.0 based protocol that standardizes how AI models interact with external capabilities. An MCP server exposes three primitive types and can be hosted locally (stdio transport) or remotely (HTTP/SSE transport). Gemini CLI is a native MCP client — it auto-discovers server capabilities when a server is configured in GEMINI.md.

The Three MCP Primitives

⚙️
Tool

An action the model can invoke. Takes parameters, produces output. Examples: query_database, send_email, create_issue. The model decides when to call a tool based on the conversation.

📄
Resource

A readable data source with a URI. Examples: file:///path/to/doc, postgres://table/users. Resources are read-only and provide context, not actions.

💬
Prompt

A reusable, parameterized prompt template. Examples: summarize_document, review_pr. Prompts are workflow shortcuts — pre-written prompts with slots for dynamic values.

Gemini CLI uses all three primitives. During a conversation, it can call Tools autonomously, expose Resources as context, and surface Prompts as slash-command shortcuts.

Why It Matters

MCP turns Gemini CLI from a code assistant into a workflow orchestrator. With the right MCP servers installed, a single prompt like "Find all open P1 issues assigned to me, check if any have PRs ready, and ping the relevant engineers on Slack" becomes executable — Gemini calls the GitHub MCP server, the Slack MCP server, and assembles the result. No scripting required.

The protocol is clear. Now let's install our first MCP server.

Adding MCP Servers to Gemini CLI

MCP servers are configured in GEMINI.md — the same memory file that holds project context. The ## MCP Servers section is a YAML block that Gemini CLI parses at startup.

# Install a community MCP server globally via npm
npm install -g @modelcontextprotocol/server-postgres

# Or install project-locally
npm install --save-dev @modelcontextprotocol/server-filesystem

# Verify the server binary is available
npx @modelcontextprotocol/server-postgres --help
# Install a community MCP server globally via npm
npm install -g @modelcontextprotocol/server-postgres

# Or install project-locally
npm install --save-dev @modelcontextprotocol/server-filesystem

# Verify the server binary is available
npx @modelcontextprotocol/server-postgres --help

GEMINI.md Configuration

Add an ## MCP Servers block to your project's GEMINI.md file:

GEMINI.md — MCP Servers block
## MCP Servers

- name: postgres
  command: npx
  args: ["@modelcontextprotocol/server-postgres"]
  env:
    DATABASE_URL: ${DATABASE_URL}

- name: filesystem
  command: npx
  args: ["@modelcontextprotocol/server-filesystem", "/home/user/projects"]

- name: github
  command: npx
  args: ["@modelcontextprotocol/server-github"]
  env:
    GITHUB_PERSONAL_ACCESS_TOKEN: ${GITHUB_TOKEN}
WHAT: Each entry specifies a server name, the command to start it, arguments, and environment variables. Gemini CLI spawns these as child processes using stdio transport.
WHY: Using ${DATABASE_URL} syntax means the actual value is read from your shell environment at startup — the secret never appears in the GEMINI.md file itself, which may be committed to git.
GOTCHA: The GEMINI.md in your project directory is committed to git. Never write literal credential values in the MCP Servers block — always use ${ENV_VAR} references.

Verifying Installed Tools

# Start Gemini CLI and list all loaded tools
gemini
# Inside the session:
/tools

# Or check from the command line (non-interactive)
gemini -p "/tools" | Out-String
# Start Gemini CLI and list all loaded tools
gemini
# Inside the session:
/tools

# Or check from the command line (non-interactive)
gemini -p "/tools"

Expected /tools output after configuring the postgres and github servers:

Output of /tools
Available tools (15):

Built-in:
  shell_execute       Execute shell commands
  file_read           Read file contents
  file_write          Write or modify files
  web_search          Search the web

MCP — postgres:
  query               Run a SELECT query against the configured database
  list_tables         List all tables in the database
  describe_table      Get schema for a specific table

MCP — github:
  create_issue        Create a GitHub issue
  list_issues         List issues with filters
  create_pull_request Create a pull request
  get_pull_request    Get PR details and diff
  list_commits        List recent commits

What Just Happened?

Gemini CLI started both MCP servers as child processes, called their tools/list endpoint via JSON-RPC, and registered the returned tool schemas. Every tool now appears as a callable function during inference. When you type "What are the 5 largest tables in the database?", Gemini autonomously calls list_tables followed by a query call — you see the answer, not the tool calls.

Animation — MCP Tool Discovery on Startup
[gemini] reading GEMINI.md...
[gemini] spawning MCP server: postgres
→ tools/list: query, list_tables, describe_table (3 tools)
[gemini] spawning MCP server: github
→ tools/list: create_issue, list_issues, create_pull_request, ... (5 tools)
[gemini] 8 MCP tools registered (2 servers)
Gemini CLI ready. Type your prompt or /help.
gemini> What are the 5 largest tables in the database?
[tool call] list_tables → [users, tasks, sessions, audit_logs, notifications]
[tool call] query → SELECT table_name, pg_size_pretty(...)
Community servers are installed. Now let's look at Google's official extension catalog.

Google + Partner Extension Catalog

Google maintains a curated set of first-party and partner-verified MCP extensions. These are pre-authenticated, production-tested integrations that go beyond community servers.

Extension Catalog

ExtensionInstall CommandWhat It Adds
Google First-Party
google-workspace gemini extensions install google-workspace Gmail, Docs, Drive, Sheets, Calendar, Meet — 100+ built-in agent skills (see next section)
cloud-run gemini extensions install google-cloud-run Deploy, list, and describe Cloud Run services; stream logs; update traffic splits
bigquery gemini extensions install google-bigquery Run SQL against BigQuery datasets; explore schemas; explain query plans
app-security gemini extensions install google-app-security Scan code for OWASP Top 10 vulnerabilities; generate security reports
Verified Partner Extensions
dynatrace gemini extensions install dynatrace Query APM metrics, traces, and logs; get AI-powered root cause analysis
figma gemini extensions install figma Read Figma designs; extract component specs; generate CSS from design tokens
postman gemini extensions install postman Run Postman collections; generate API tests; read environment variables
stripe gemini extensions install stripe Query customers, subscriptions, invoices; test webhooks; diagnose payment failures
shopify gemini extensions install shopify Read product catalogs, orders, inventory; generate storefront code snippets
Tip — Extensions vs. Community Servers

Extensions installed via gemini extensions install are verified by Google: they follow MCP security best practices, handle OAuth automatically, and are updated alongside Gemini CLI. Community servers installed via npm install + GEMINI.md give more flexibility but require manual credential management and updates.

The extension catalog exists. The Google Workspace extension deserves its own deep dive.

The Google Workspace Extension

Analogy — The Personal Assistant Who Has Your Calendar Open

A highly effective executive assistant doesn't just answer questions — they have your Gmail, Calendar, and Drive open simultaneously and act across all of them. "Send the meeting notes to the team" doesn't require three separate steps; they draft the email, find the notes in Drive, and send it while you're on the next call.

The pain without the Workspace extension: you copy content from Gmail into Gemini CLI, Gemini CLI generates a response, you copy the response back into Gmail. The mapping: the Workspace extension means Gemini CLI reads your Gmail directly, generates the response, and sends it — one prompt, one action.

Installation and Authentication

# Install the Workspace extension
gemini extensions install google-workspace

# Output: Installing google-workspace...
#   Requesting OAuth scopes:
#     - https://www.googleapis.com/auth/gmail.readonly
#     - https://www.googleapis.com/auth/gmail.send
#     - https://www.googleapis.com/auth/drive.readonly
#     - https://www.googleapis.com/auth/calendar.events
#   Opening browser for authorization...
#   ✓ Authorized. Token stored in: ~\.gemini\extensions\google-workspace\token.json

# Verify installation
gemini extensions list
# Install the Workspace extension
gemini extensions install google-workspace

# Output: Installing google-workspace...
#   Requesting OAuth scopes:
#     - https://www.googleapis.com/auth/gmail.readonly
#     - https://www.googleapis.com/auth/gmail.send
#     - https://www.googleapis.com/auth/drive.readonly
#     - https://www.googleapis.com/auth/calendar.events
#   Opening browser for authorization...
#   ✓ Authorized. Token stored in: ~/.gemini/extensions/google-workspace/token.json

# Verify installation
gemini extensions list

Once installed, Gemini gains over 100 built-in agent skills spanning all Workspace products:

ServiceExample Skills
Gmail Read inbox, search by sender/subject, draft and send emails, label and archive, read threads
Google Docs Read document content, create documents, append text, export as markdown/PDF
Google Drive Search files, list recent, share with permissions, read file metadata
Google Sheets Read cell ranges, append rows, create sheets, run named ranges, format cells
Google Calendar List today's events, create events, find free slots, check attendee availability

Cross-Service Example: Gmail + Slack

The real power emerges when Workspace skills combine with other MCP servers. With both google-workspace and a Slack MCP server installed:

Gemini CLI prompt — multi-service workflow
Send a summary of today's unread emails from my product@company.com address
to the #product-updates Slack channel. Group by sender. Include action items.

Gemini's execution chain (invisible to the user):

  1. Calls Gmail list_messages with filter to:product@company.com is:unread newer_than:1d
  2. Calls Gmail get_message for each message to read full content
  3. Generates a grouped summary with action items extracted from each thread
  4. Calls Slack MCP post_message to #product-updates with the formatted summary
Gotcha — Scope Review Matters

The installation process requests specific OAuth scopes. Before clicking "Allow", review which scopes are being requested. The gmail.send scope means Gemini can send emails as you. The drive.file scope is more restricted than drive.readonly — prefer the most restrictive scope that allows the workflows you need.

Local and installed extensions are working. Remote MCP servers add another dimension.

OAuth 2.0 and Remote MCP Servers

Not all MCP servers run locally. Remote MCP servers are hosted services that expose their capabilities over HTTPS using Server-Sent Events (SSE)An HTTP-based protocol where the server pushes a stream of events to the client over a persistent connection. MCP uses SSE as the transport for remote servers, allowing the server to send tool responses back to Gemini CLI asynchronously over a single long-lived HTTP connection. transport. Accessing them requires OAuth 2.0 for authentication.

GEMINI.md — remote MCP server with OAuth
## MCP Servers

- name: linear
  type: remote
  url: https://mcp.linear.app/sse
  auth:
    type: oauth2
    client_id: ${LINEAR_MCP_CLIENT_ID}
    client_secret: ${LINEAR_MCP_CLIENT_SECRET}
    scopes: ["issues:read", "issues:write", "teams:read"]

- name: notion
  type: remote
  url: https://api.notion.com/mcp/sse
  auth:
    type: bearer
    token: ${NOTION_INTEGRATION_TOKEN}
WHAT: The type: remote field tells Gemini CLI to connect via HTTP/SSE rather than spawning a child process. The auth block configures the credential exchange before the first tool call.
WHY: Remote MCP servers are hosted by the service provider — you don't need to install or run anything locally. Linear, Notion, and similar services can maintain their server and you get automatic updates.

Environment Variable Sanitization

By default, Gemini CLI passes all environment variables to MCP server processes. This is a security risk if you install an untrusted community server — it could read your GITHUB_TOKEN, AWS_SECRET_ACCESS_KEY, or DATABASE_URL.

Security — Prevent Credential Leakage

Use the env_allowlist configuration to explicitly restrict which environment variables a server process can see. Never install an untrusted MCP server without reviewing its source code — a malicious server can exfiltrate any environment variable it receives.

GEMINI.md — env sanitization with allowlist
## MCP Servers

# Untrusted community server — restrict env access
- name: community-analytics
  command: npx
  args: ["community-analytics-mcp-server"]
  env:
    # Only pass the vars this server legitimately needs
    ANALYTICS_API_KEY: ${ANALYTICS_API_KEY}
    NODE_ENV: production
  # Prevent inheriting the parent shell's environment entirely
  env_inherit: false
WHAT: env_inherit: false blocks the process from seeing any shell environment variable except those explicitly listed in the env block.
GOTCHA: Without env_inherit: false, a community server process can read process.env.DATABASE_URL (Node.js) or os.environ["AWS_SECRET_ACCESS_KEY"] (Python) freely. This is not a Gemini CLI bug — it's standard Unix process behavior. The sanitization opt-in protects against it.
You've installed and secured external MCP servers. Now let's build one yourself.

Building a Custom MCP Server

Analogy — Writing a Plugin

Every major software platform has a plugin system: VS Code extensions, Figma plugins, Chrome extensions. You write code against a defined API, package it, and the host application runs it. MCP is Gemini CLI's plugin system — and FastMCP makes writing a plugin as simple as decorating a Python function.

The pain: building against the raw MCP JSON-RPC protocol requires handling capability negotiation, tool schema generation, and message routing manually. The mapping: FastMCP is a Python library where @mcp.tool() turns any function into a fully spec-compliant MCP tool — it handles the protocol plumbing so you write business logic.

FastMCP in 20 Lines — Python

# Install: pip install fastmcp
from fastmcp import FastMCP
import httpx

mcp = FastMCP("company-data-server")

@mcp.tool()
def get_customer(customer_id: str) -> dict:
    """Look up a customer by ID from the internal CRM API."""
    with httpx.Client() as client:
        response = client.get(
            f"https://crm.internal/api/customers/{customer_id}",
            headers={"Authorization": f"Bearer {os.environ['CRM_API_KEY']}"}
        )
        response.raise_for_status()
        return response.json()

@mcp.tool()
def list_open_orders(customer_id: str, limit: int = 10) -> list[dict]:
    """List open orders for a customer, newest first. Max 100."""
    limit = min(limit, 100)  # enforce ceiling
    with httpx.Client() as client:
        r = client.get(
            f"https://orders.internal/api/orders",
            params={"customer_id": customer_id, "status": "open", "limit": limit}
        )
        r.raise_for_status()
        return r.json()["orders"]

if __name__ == "__main__":
    mcp.run()  # stdio transport (default)
// Install: npm install @modelcontextprotocol/sdk
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "company-data-server", version: "1.0.0" });

server.tool(
  "get_customer",
  "Look up a customer by ID from the internal CRM API",
  { customer_id: z.string().describe("The customer UUID") },
  async ({ customer_id }) => {
    const res = await fetch(
      `https://crm.internal/api/customers/${customer_id}`,
      { headers: { Authorization: `Bearer ${process.env.CRM_API_KEY}` } }
    );
    if (!res.ok) throw new Error(`CRM returned ${res.status}`);
    const data = await res.json();
    return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
WHAT: The FastMCP @mcp.tool() decorator reads the function's docstring (tool description), type annotations (parameter schema), and return type to generate the full MCP tool definition automatically.
WHY: Gemini uses the docstring to decide when to call the tool. A precise docstring ("Look up a customer by ID from the internal CRM API") is more important than the code — write it first.
GOTCHA: The TypeScript version requires the MCP SDK's StdioServerTransport to handle stdio communication. The server.connect(transport) call must be awaited at the module level — if you put it inside an Express route handler, it will never run.

Connect your new server to Gemini CLI via GEMINI.md:

GEMINI.md — connecting the custom server
## MCP Servers

- name: company-data
  command: python
  args: ["tools/mcp_server.py"]
  env:
    CRM_API_KEY: ${CRM_API_KEY}
  env_inherit: false
Animation — Custom MCP Tool Call During Inference
GEMINI CLI (CLIENT)
User: "What are the open orders for customer cust-0042?"
→ Calls list_open_orders(customer_id="cust-0042")
Response received: 3 open orders
Gemini: "Customer cust-0042 has 3 open orders: ORD-1, ORD-2, ORD-3"
MCP SERVER (company-data)
[waiting for JSON-RPC request...]
Received: tools/call list_open_orders
→ GET orders.internal/api/orders?customer_id=cust-0042
← 200 OK: [{id:"ORD-1",...}, {id:"ORD-2",...}, {id:"ORD-3",...}]

What Just Happened?

The animation shows the JSON-RPC call sequence between Gemini CLI and the custom MCP server. The user's natural language question triggered an autonomous tool call — the user never wrote list_open_orders in their prompt. Gemini inferred the right tool from the docstring and the conversation context.

Animation — Gemini CLI MCP Architecture
Gemini CLI (MCP Client)
postgres
query, list_tables
github
issues, PRs, commits
google-workspace
Gmail, Docs, Drive
company-data
CRM, orders
stripe
customers, subscriptions
25+ tools registered — one conversation, all your systems
Why It Matters

The MCP ecosystem is growing faster than any previous integration standard in developer tooling. As of mid-2025, over 5,000 MCP servers are publicly available. Every server you add multiplies what Gemini CLI can do without changing the model or writing new prompts — the context window becomes a unified interface to your entire digital environment.

Knowledge Check

1. What are the three MCP primitives and what distinguishes a Tool from a Resource?

A
Tool = read-only; Resource = action; Prompt = configuration file
B
Tool = an action the model can invoke (takes parameters, produces output); Resource = a readable data source with a URI (read-only context); Prompt = a reusable parameterized prompt template
C
Tool = Python function; Resource = database table; Prompt = GEMINI.md instruction
D
The three primitives are Server, Client, and Transport — Tools and Resources are subtypes of Server

2. Why is the docstring on a FastMCP @mcp.tool() function more important than the function name?

A
Because FastMCP uses the docstring as the function name at runtime
B
Because the MCP protocol requires docstrings for type validation
C
Gemini uses the docstring (tool description) to decide when to autonomously call the tool — a precise description directly determines whether the model chooses the right tool for the user's intent
D
Because Python does not allow underscores in function names used as MCP tools

3. What security risk does env_inherit: false prevent when running an untrusted MCP server?

A
It prevents the MCP server from making outbound network requests
B
Without it, the MCP server child process inherits all environment variables from the parent shell — including database credentials, cloud provider keys, and API tokens that it should never see
C
It prevents the server from reading files outside the current working directory
D
It prevents multiple MCP servers from sharing environment variables with each other

4. What is the correct way to include a database credential in an MCP server configuration in GEMINI.md?

A
Write the credential directly: DATABASE_URL: postgresql://user:password@host/db
B
Encrypt the credential with gemini secrets encrypt before adding it to GEMINI.md
C
Use the ${ENV_VAR} reference syntax: DATABASE_URL: ${DATABASE_URL} — the value is read from the shell environment at startup, never written to the GEMINI.md file
D
Store credentials in a separate .gemini-secrets.yaml file that GEMINI.md references by filename

5. What is the key difference between a verified Google partner extension and a community MCP server installed via npm?

A
Partner extensions are faster because they use binary protocols instead of JSON-RPC
B
Partner extensions are verified for MCP security best practices, handle OAuth automatically via gemini extensions install, and receive updates managed by Google — community servers require manual credential management and version updates
C
Community servers support more features than partner extensions because they are not restricted by Google's API policies
D
Partner extensions only work with Gemini 2.0 Pro; community servers work with all Gemini models

6. When configuring a remote MCP server with OAuth 2.0, what transport protocol does Gemini CLI use to communicate with it?

A
WebSocket — because remote servers require bidirectional real-time communication
B
stdio — the same transport as local servers, tunneled through an SSH connection
C
HTTP with Server-Sent Events (SSE) — a persistent HTTP connection over which the server pushes tool responses back to Gemini CLI asynchronously
D
gRPC — because JSON-RPC is too slow for production remote servers