
The Gemini CLI can be integrated with Google Workspace via Google Apps Script to securely access personal data, enabling powerful automations like email summaries and calendar management.
The recently released Gemini CLI is a powerful command-line interface for interacting with Google’s Gemini models and cloud resources. Ref While powerful on its own, its utility can be significantly enhanced by connecting it to a user’s personal Google resources, such as Google Sheets, Docs, Slides, Gmail, and Calendar.
Google Apps Script emerges as the ideal solution for this integration. It natively handles the complex OAuth2 authorization process, allowing for secure and seamless access to a user’s Google Workspace data. This is achieved by deploying an Apps Script project as a Web App, which effectively creates a secure API endpoint. To standardize this communication, a recent report details how to build a Model Context Protocol (MCP) server using this Web App approach. Ref This method is particularly advantageous as it is cost-effective and leverages the familiar Google ecosystem, requiring no separate cloud infrastructure.
By integrating this MCP server with the Gemini CLI, the tool is transformed. It gains the ability to ground its responses and actions in the user’s specific context, allowing for powerful automations like summarizing recent emails, creating documents from prompts, or managing calendar events directly from the command line. This report provides a step-by-step guide on how to implement this integration, unlocking the full potential of the Gemini CLI by securely connecting it to your Google Workspace environment.
In this usage, first, the MCP server is deployed and tested with the Gemini CLI.
First, create a new standalone Google Apps Script project. A standalone project is not bound to a specific Google Sheet, Doc, or Form, making it ideal for creating a general-purpose web service. You can create one by visiting script.google.com. Ref
Repository
https://github.com/tanaikech/MCPApp
Library’s project key
1TlX_L9COAriBlAYvrMLiRFQ5WVf1n0jChB6zHamq2TNwuSbVlI5sBUzh
To simplify building the MCP server, we will use a pre-built Google Apps Script library. This encapsulates the complex MCP handling logic, keeping your main script clean and focused on defining the custom tools.
- Open the script editor of the project you just created.
- Install this library: The library’s project key is
1TlX_L9COAriBlAYvrMLiRFQ5WVf1n0jChB6zHamq2TNwuSbVlI5sBUzh
. UseMCPApp
as the identifier.
Please copy and paste the following script to the script editor and save the script. This script defines two custom tools: get_exchange_rate
and get_current_weather
, which the Gemini CLI will be able to invoke.
function getObjects_() {
const functions = {
// This 'params_' object defines the schema for the available tools, following the Gemini tool-calling specification.
params_: {
get_exchange_rate: {
description:
"Use this to get the current exchange rate. Using this, for example, it can exchange yen for dollars.",
parameters: {
type: "object",
properties: {
currency_from: {
type: "string",
description: "Source currency (major currency). Default is USD.",
},
currency_to: {
type: "string",
description:
"Destination currency (major currency). Default is EUR.",
},
currency_date: {
type: "string",
description:
"Date of the currency. Default is latest. It should be ISO format (YYYY-MM-DD).",
},
},
required: ["currency_from", "currency_to", "currency_date"],
},
},get_current_weather: {
description: [
"Use this to get the weather using the latitude and the longitude.",
"At that time, convert the location to the latitude and the longitude and provide them to the function.",
`The date is required to be included. The date format is "yyyy-MM-dd HH:mm"`,
`If you cannot know the location, decide the location using the timezone.`,
].join("\n"),
parameters: {
type: "object",
properties: {
latitude: {
type: "number",
description: "The latitude of the inputed location.",
},
longitude: {
type: "number",
description: "The longitude of the inputed location.",
},
date: {
type: "string",
description: `Date for searching the weather. The date format is "yyyy-MM-dd HH:mm"`,
},
timezone: {
type: "string",
description: `The timezone. In the case of Japan, "Asia/Tokyo" is used.`,
},
},
required: ["latitude", "longitude", "date", "timezone"],
},
},
},
/**
* This function implements the get_exchange_rate tool.
* Ref: https://github.com/google/A2A/blob/main/samples/python/agents/langgraph/agent.py#L19
*/
get_exchange_rate: (object) => {
console.log("Run the function get_exchange_rate.");
console.log(object); // Check arguments.
const {
currency_from = "USD",
currency_to = "EUR",
currency_date = "latest",
} = object;
let res;
try {
const resStr = UrlFetchApp.fetch(
`https://api.frankfurter.app/${currency_date}?from=${currency_from}&to=${currency_to}`
).getContentText();
const obj = JSON.parse(resStr);
res = [
`The raw data from the API is ${resStr}. The detailed result is as follows.`,
`The currency rate at ${currency_date} from "${currency_from}" to "${currency_to}" is ${obj.rates[currency_to]}.`,
].join("\n");
} catch ({ stack }) {
res = stack;
}
console.log(res); // Check response.
return {
jsonrpc: "2.0",
result: { content: [{ type: "text", text: res }], isError: false },
};
},
/**
* This function implements the get_current_weather tool.
* The API is from https://open-meteo.com/
*
* { latitude = "35.681236", longitude = "139.767125", date = "2025-05-27 12:00", timezone = "Asia/Tokyo" } is Tokyo station.
*/
get_current_weather: (object) => {
console.log("Run the function get_current_weather.");
console.log(object); // Check arguments.
const {
latitude = "35.681236",
longitude = "139.767125",
date = "2025-05-27 12:00",
timezone = "Asia/Tokyo",
} = object;
let res;
try {
// Ref: https://open-meteo.com/en/docs?hourly=weather_code¤t=weather_code#weather_variable_documentation
const code = {
0: "Clear sky",
1: "Mainly clear, partly cloudy, and overcast",
2: "Mainly clear, partly cloudy, and overcast",
3: "Mainly clear, partly cloudy, and overcast",
45: "Fog and depositing rime fog",
48: "Fog and depositing rime fog",
51: "Drizzle: Light, moderate, and dense intensity",
53: "Drizzle: Light, moderate, and dense intensity",
55: "Drizzle: Light, moderate, and dense intensity",
56: "Freezing Drizzle: Light and dense intensity",
57: "Freezing Drizzle: Light and dense intensity",
61: "Rain: Slight, moderate and heavy intensity",
63: "Rain: Slight, moderate and heavy intensity",
65: "Rain: Slight, moderate and heavy intensity",
66: "Freezing Rain: Light and heavy intensity",
67: "Freezing Rain: Light and heavy intensity",
71: "Snow fall: Slight, moderate, and heavy intensity",
73: "Snow fall: Slight, moderate, and heavy intensity",
75: "Snow fall: Slight, moderate, and heavy intensity",
77: "Snow grains",
80: "Rain showers: Slight, moderate, and violent",
81: "Rain showers: Slight, moderate, and violent",
82: "Rain showers: Slight, moderate, and violent",
85: "Snow showers slight and heavy",
86: "Snow showers slight and heavy",
95: "Thunderstorm: Slight or moderate",
96: "Thunderstorm with slight and heavy hail",
99: "Thunderstorm with slight and heavy hail",
};
const endpoint = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=weather_code&timezone=${encodeURIComponent(
timezone
)}`;
const resObj = UrlFetchApp.fetch(endpoint, {
muteHttpExceptions: true,
});
if (resObj.getResponseCode() == 200) {
const obj = JSON.parse(resObj.getContentText());
const {
hourly: { time, weather_code },
} = obj;
const widx = time.indexOf(date.replace(" ", "T").trim());
if (widx != -1) {
res = code[weather_code[widx]];
} else {
res = "No value was returned. Please try again.";
}
} else {
res = "No value was returned. Please try again.";
}
} catch ({ stack }) {
res = stack;
}
console.log(res); // Check response.
return {
jsonrpc: "2.0",
result: { content: [{ type: "text", text: res }], isError: false },
};
},
};
// The following structure is required by the MCPApp library to define the server's capabilities and available tools.
const itemsForMCP = [
{
type: "initialize",
value: {
protocolVersion: "2024-11-05", // or "2025-03-26"
capabilities: { tools: { listChanged: false } },
serverInfo: { name: "gas_web_apps", version: "1.0.0" },
},
},
...Object.keys(functions.params_).map((f) => ({
type: "tools/list",
function: functions[f],
value: {
name: f,
description: functions.params_[f].description,
inputSchema: functions.params_[f].parameters,
},
})),
];
return itemsForMCP;
}
/**
* This is the main entry point for the Web App. It's automatically run when the Gemini CLI makes a POST request.
*/
const doPost = (e) => main(e);
function main(eventObject) {
const object = { eventObject, items: getObjects_() };
// The 'accessKey' provides a simple layer of security. Ensure this key matches the one in your Gemini CLI configuration.
return new MCPApp.mcpApp({ accessKey: "sample" })
.setServices({ lock: LockService.getScriptLock() })
.server(object);
}
To allow the Gemini CLI to communicate with our script, we must deploy it as a Web App. This creates a unique URL that acts as our MCP server endpoint.
You can find detailed information in the official documentation.
Please follow these steps to deploy the Web App in the script editor:
- In the script editor, at the top right, click Deploy -> New deployment.
- Click Select type -> Web App.
- Enter a description for the Web App in the fields under Deployment configuration.
- Select “Me” for “Execute as”. This is crucial, as it allows the script to run with your permissions to access your Google services.
- Select “Anyone” for “Who has access to the app:”. This makes the URL callable from the internet. Access is still controlled by the unguessable URL and the
accessKey
defined in the script. - Click Deploy.
- After authorizing the necessary scopes, copy the Web app URL. It will be similar to
https://script.google.com/macros/s/###/exec
. This is your MCP server endpoint.
Note: When you modify the Apps Script code, you must create a new deployment to publish the changes. Click Deploy > Manage deployments, select your active deployment, click the pencil icon, and choose “New version” from the Version dropdown. More info here.
Follow the official documentation to install the Gemini CLI on your system. Ref
To connect Gemini CLI to your new Apps Script server, you need to edit its settings file. This file is typically located at ~/.gemini/settings.json
on macOS/Linux or %USERPROFILE%\.gemini\settings.json
on Windows.
Add the mcpServers
configuration block as shown below. Replace https://script.google.com/macros/s/###/exec
with the Web App URL you copied earlier.
{
"theme": "Default",
"selectedAuthType": "###",
"mcpServers": {
"gas_web_apps": {
"command": "npx",
"args": [
"mcp-remote",
"https://script.google.com/macros/s/###/exec?accessKey=sample"
],
"env": {}
}
}
}
"gas_web_apps"
: A local name for your server."command"
and"args"
: These tell the Gemini CLI how to invoke the MCP remote tool, which then communicates with your Web App URL.accessKey=sample
: This query parameter must exactly match theaccessKey
you set in your Google Apps Scriptmain
function.
When both the MCP server and Gemini CLI are correctly prepared, you can test the integration. When you issue a prompt, the Gemini CLI will intelligently call your Web App to use the custom tools when needed to fulfill the request.
This demonstration runs as follows:
- Run Gemini CLI in interactive mode:
gemini
- Confirm the available MCP server is recognized:
/mcp list
- Run a prompt that requires the currency tool:
How much is 10,000 yen in USD?
Behind the scenes, Gemini CLI calls your Web App, which executes theget_exchange_rate
function and returns the result. Gemini then uses this result to formulate the final answer. - Run a prompt that requires the weather tool:
What is the weather forecast for Tokyo at noon today?
Similarly, the CLI calls theget_current_weather
function via your Web App to get the necessary data.
When using non-interactive mode, the commands are simpler:
gemini -p "How much is 10,000 yen in USD?"
gemini -p "What is the weather forecast for Tokyo at noon today?"
- This report explained how to integrate the Gemini CLI with your personal Google Workspace data (like Gmail and Calendar) for powerful automations.
- The integration was achieved by building a Model Context Protocol (MCP) server using a Google Apps Script project deployed as a Web App.
- This method leveraged Google Apps Script to securely handle authorization, allowing Gemini to use custom-defined tools to access your data without requiring separate cloud infrastructure.
- The guide provided the full Apps Script code, including example functions for getting currency exchange rates and weather forecasts, to demonstrate how to define tools for Gemini.
- Setup involved deploying the script as a Web App and configuring the Gemini CLI’s
settings.json
file to connect to the new Web App URL, which acted as the MCP server endpoint.
Source Credit: https://medium.com/google-cloud/gemini-cli-with-mcp-server-built-by-web-apps-of-google-apps-script-47046afdf3be?source=rss—-e52cf94d98af—4