The gas-fakes library is a powerful tool for safely executing Google Apps Script in a local Node.js environment, functioning as a sandboxed runtime. This is particularly beneficial when running Google Apps Script generated by AI, as it mitigates the security risks associated with the broad permissions that scripts often require. gas-fakes enhances security by emulating the Apps Script environment through the translation of GAS service calls into their equivalent, underlying Google API requests, which allows for more granular and file-specific permission controls.
Until now, gas-fakes has primarily been used as a library within Node.js scripts. Recognizing the potential for broader application and improved developer experience, a command-line interface (CLI) tool has been created. This CLI tool simplifies the use of gas-fakes and can also be used as an MCP (Model Context Protocol) server. This functionality is motivated by the idea that it will enhance its application, especially for tasks like conversational automation of Google Workspace when integrated with tools like the Gemini CLI.
See the Getting Started for more information on getting started with authentication.
To install the gas-fakes CLI tool, run the following command in your terminal:
npm install -g @mcpher/gas-fakesgas-fakes supports multiple authentication backends, including Google Workspace and Infomaniak KSuite.
You can use the gas-fakes command-line interface (CLI) to assist with the setup.
First, create a .env file to store your project configuration. The init command will prompt you to select the backends you wish to use (Google, KSuite, or both).
To initialize Google with Domain Wide Delegation (default):
gas-fakes initTo initialize Google with Application Default Credentials (ADC):
gas-fakes init --auth-type adcTo initialize multiple backends at once (e.g., Google, KSuite, and MS Graph):
gas-fakes init -b google -b ksuite -b msgraphNext, authorize the tool. This command will guide you through the process of logging into your accounts. By default, it authenticates Google.
gas-fakes authTo authenticate a specific backend like KSuite or MS Graph:
gas-fakes auth --backend ksuite
# or
gas-fakes auth --backend msgraphIf you need to enable the required Google APIs for your project, you can do so with the following command:
gas-fakes enableAPIs --helpNote on Scopes: Starting with v2.1.0,
gas-fakesautomatically discovers required scopes by reading yourappsscript.jsonfile, simplifying the setup for both ADC and DWD. Note on .env: Your active platforms and configuration are stored in the.envfile.gasfakes.jsonis no longer used as of v2.2.0.
To see the available commands and options, run gas-fakes with no arguments:
gas-fakes --helpThis will display the following help message:
$ gas-fakes --help
Usage: gas-fakes [options] [command]
Execute a Google Apps Script file or string.
Options:
--at,--auth-type <string> The authentication type to use. ("adc" or "dwd", default: "dwd")
-v, --version Display the current version of gas-fakes
-f, --filename <string> Path to the Google Apps Script file.
-s, --script <string> A string containing the Google Apps Script.
-e, --env <path> Path to a custom .env file. (default: "./.env")
-x, --sandbox Run the script in a basic sandbox.
> **Note on .env files:** `gas-fakes` also supports the native Node.js `--env-file` flag (available in Node.js 20.6.0+). If you run `node --env-file=.env gas-fakes ...`, the CLI will respect those variables and skip loading its default `./.env` file. Explicitly providing `-e` will still take precedence.
-w, --whitelistRead <string> Comma-separated file IDs for read-only access (enables sandbox).
--ww, --whitelistReadWrite <string> Comma-separated file IDs for read/write access (enables sandbox).
--wt, --whitelistReadWriteTrash <string> Comma-separated file IDs for read/write/trash access (enables sandbox).
-j, --json <string> JSON string for advanced sandbox configuration (overrides whitelist flags).
-d, --display Display the generated script before execution. (default: false)
-a, --args <string> Arguments for the function of Google Apps Script. Provide it as a JSON string. The name of the argument is "args" as
a fixed name. For example, when the function of GAS is `function sample(args) { script }`, you can provide the
arguments like `-a '{"key": "value"}'`. (default: null)
-l, --libraries <string...> Libraries. You can run the Google Apps Script with libraries. When you use 2 libraries "Lib1" and "Lib" which are
the identifiers of library, provide '--libraries "Lib1@{filename}" --libraries "Lib2@{file URL}"'. (default: null)
-h, --help display help for command
Commands:
init [options] Initializes the configuration by creating or updating the .env file.
auth Runs the Google Cloud authentication and authorization flow.
enableAPIs [options] Enables or disables required Google Cloud APIs for the project.
mcp [options] Launch gas-fakes as an MCP server.
You can execute a script from a local file using the -f or --filename option.
Example:
-
Create a file named
sample1.jswith the following content:const rootFolder = DriveApp.getRootFolder(); const rootFolderName = rootFolder.getName(); console.log(rootFolderName);
-
Run the following command:
gas-fakes -f sample1.js
For shorter scripts, you can pass the code directly as a string using the -s or --script option.
Example:
gas-fakes -s "const rootFolder = DriveApp.getRootFolder(); const rootFolderName = rootFolder.getName(); console.log(rootFolderName)"This will produce the same output as the file-based example.
Starting with version 2.1.0, gas-fakes supports Infomaniak KSuite (kDrive) alongside Google Workspace. This allows you to run standard Google Apps Script code against non-Google platforms by simply switching the "platform" in your script.
You can steer gas-fakes to use a specific backend by setting the ScriptApp.__platform property.
ScriptApp.__platform = 'google'(Default): Targets Google APIs.ScriptApp.__platform = 'ksuite': Targets Infomaniak KSuite APIs.
Example:
// Check auth status
if (ScriptApp.__isPlatformAuthed('google')) {
console.log('Google is authorized');
}
console.log('Authorized platforms:', ScriptApp.__platforms);
// Switch to KSuite
ScriptApp.__platform = 'ksuite';
// This standard GAS code now runs against Infomaniak kDrive!
const root = DriveApp.getRootFolder();
console.log("KSuite Root Folder:", root.getName());
const folder = root.createFolder("KSuite-Test");
folder.createFile("hello.txt", "This was created on kDrive via standard GAS code.");- Initialize the KSuite backend:
gas-fakes init -b ksuite
- Add your Infomaniak API token to your
.envfile:KSUITE_TOKEN=your_infomaniak_api_token
- Ensure your
GF_PLATFORM_AUTHincludesksuite.
For more details, see the KSuite POC documentation.
A key feature of gas-fakes is its ability to run scripts in a sandbox, which is a restricted environment that prevents the script from accessing unauthorized resources. This is especially important for running code from untrusted sources.
To enable the sandbox, use the -x or --sandbox option.
Example:
gas-fakes -x -f sample1.jsWhen you run this command, you will see a more verbose output that details the sandbox setup process:
$ gas-fakes -x -f sample1.js
[Worker] ...importing Drive API
[Worker] ...importing Sheets API
[Worker] ...importing Slides API
[Worker] ...using env file in /workspace/.env
[Worker] ...cache will be in /tmp/gas-fakes/cache
[Worker] ...properties will be in /tmp/gas-fakes/properties
[Worker] ...initializing auth and discovering project ID
[Worker] ...discovered and set projectId to for-testing
[Worker] Creating new Drive API client
MyDrive
...trashed 0 sandboxed files
...terminating worker thread
By default, a sandboxed script cannot access any of your files. To grant access to specific files, you can use the -w or --whitelist option, providing a comma-separated list of file IDs.
Example:
-
Create a file named
sample2.jswith the following content. Replace###with a file ID from your Google Drive.const fileId = "###"; const file = DriveApp.getFileById(fileId); const name = file.getName(); console.log(name);
-
If you run this in a sandbox without whitelisting, you will get an error:
gas-fakes -x -f sample2.js
Output:
Error: Access to file ### is denied by sandbox rules -
To grant access, add the file ID to the whitelist:
gas-fakes -x -f sample2.js -w "###"This time, the script will execute successfully and print the name of the file.
For more fine-grained control over the sandbox, you can use the -j or --json option to provide a JSON object that specifies permissions.
Example:
Using the same sample2.js file, you can define specific permissions for items and services:
gas-fakes -x -f sample2.js -j '{"whitelistItems":[{"itemId":"###"}],"whitelistServices":[{"className":"DriveApp","methodNames":["getFileById"]}]}'In this example, the sandbox only allows access to the specified file ID (itemId) and only permits the use of the getFileById method from the DriveApp service.
The JSON object used with the -j option follows this schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Whitelist and Blacklist Configuration",
"description": "A configuration for whitelisting and blacklisting items and services.",
"type": "object",
"properties": {
"whitelistItems": {
"description": "A list of items to be whitelisted.",
"type": "array",
"items": {
"type": "object",
"properties": {
"itemId": {
"description": "The file ID and folder ID of the file on Google Drive.",
"type": "string"
},
"read": {
"description": "Read permission for the item. Default is true.",
"type": "boolean"
},
"write": {
"description": "Write permission for the item. Default is false.",
"type": "boolean"
},
"trash": {
"description": "Trash permission for the item. Default is false.",
"type": "boolean"
}
},
"required": ["itemId"]
}
},
"gmailSandbox": {
"description": "Configuration for Gmail sandbox settings.",
"type": "object",
"properties": {
"emailWhitelist": {
"description": "List of email addresses allowed to receive emails. Emails sent to addresses not in this list (or allowed by session rules) will throw an error.",
"type": "array",
"items": {
"description": "List of email addresses allowed to receive emails. Emails sent to addresses not in this list (or allowed by session rules) will throw an error.",
"type": "string"
}
},
"usageLimit": {
"description": "Limits for operations. Can be a number (implies total limit for all operations combined) or an object { read?: number, write?: number, trash?: number, send?: number }.",
"type": ["number", "object"]
},
"labelWhitelist": {
"description": "Configuration for allowed labels, specifying name and permissions (read, write, delete, send).",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"description": "Label name",
"type": "string"
},
"read": {
"description": "read",
"type": "boolean"
},
"write": {
"description": "write",
"type": "boolean"
},
"delete": {
"description": "delete",
"type": "boolean"
},
"send": {
"description": "send",
"type": "boolean"
}
}
}
},
"cleanup": {
"description": "Controls whether Gmail artifacts (labels, threads) created in the session are trashed on cleanup. Defaults to global setting if not set.",
"type": "boolean"
}
}
},
"calendarSandbox": {
"description": "Configuration for Calendar sandbox settings.",
"type": "object",
"properties": {
"calendarWhitelist": {
"description": "Configuration for allowed calendars, specifying name and permissions (read, write, delete).",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"description": "Calendar name",
"type": "string"
},
"read": {
"description": "read",
"type": "boolean"
},
"write": {
"description": "write",
"type": "boolean"
},
"delete": {
"description": "delete",
"type": "boolean"
}
},
"required": ["name"]
}
},
"usageLimit": {
"description": "Limits for operations. Can be a number (implies total limit for all operations combined) or an object { read?: number, write?: number, trash?: number }.",
"type": ["number", "object"]
},
"cleanup": {
"description": "Controls whether calendars created in the session are deleted on cleanup. Defaults to global setting if not set.",
"type": "boolean"
}
}
},
"whitelistServices": {
"description": "A list of services to be whitelisted.",
"type": "array",
"items": {
"type": "object",
"properties": {
"className": {
"description": "The name of the class of the service.",
"type": "string"
},
"methodNames": {
"description": "A list of method names for the class to be whitelisted.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["className"]
}
},
"blacklistServices": {
"description": "A list of services to be blacklisted.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["whitelistItems"]
}You might have a situation that you want to run the GAS function by providing arguments. In that case, you can use as follows.
gas-fakes -s "function sample(obj) {console.log(obj);} sample(args);" -a '{"key": "value"}'By this, the following result is returned.
$ gas-fakes -s "function sample(obj) {console.log(obj);} sample(args);" -a '{"key": "value"}'
...using env file in .env
[Worker] ...importing Drive API
[Worker] ...importing Sheets API
[Worker] ...importing Slides API
[Worker] ...cache will be in /tmp/gas-fakes/cache
[Worker] ...properties will be in /tmp/gas-fakes/properties
[Worker] ...initializing auth and discovering project ID
[Worker] ...discovered and set projectId to for-testing
[Worker] Creating new Drive API client
{"key": "value"}
...terminating worker thread
This simple example demonstrates how to use a local value in a Google Apps Script and receive a value in return.
gas-fakes -a '{"key":"sample"}' -s 'const res = "The provided argument is " + args.key; return JSON.stringify({output: res});' | grep '^{"output":' | jq -r '.output'When this command is run, the following result is returned. You can see that the provided argument is used in the Google Apps Script to return the value.
The provided argument is sample
gas-fakes CLI can run Google Apps Script using libraries as follows.
1. Create a library file
Create a sample library file named sampleLib.js by copying and pasting the following script.
function function1() {
return "function1";
}
var value1 = "value1";2. Create a main script
Create a sample script named sample.js that utilizes the library. In this example, the library identifier is defined as LIB.
const res1 = LIB.function1();
const res2 = LIB.value1;
console.log({ res1, res2 });3. Run the script
Execute the following command to link the library file to the identifier LIB and run the script:
gas-fakes -f sample.js -l LIB@sampleLib.jsLIB@sampleLib.js: LIB and sampleLib.js are the identifier and the file of library, respectively. At gas-fakes CLI, the file, the hyperlink, and the library key (File ID) of Google Apps Script library can be used.
Output:
$ gas-fakes -f sample.js -l LIB@sampleLib.js
...using env file in /workspace/.env
[Worker] ...importing Drive API
[Worker] ...importing Sheets API
[Worker] ...importing Slides API
[Worker] ...cache will be in /tmp/gas-fakes/cache
[Worker] ...properties will be in /tmp/gas-fakes/properties
[Worker] ...initializing auth and discovering project ID
[Worker] ...discovered and set projectId to for-testing
...gas-fakes version 1.2.##
{ res1: 'function1', res2: 'value1' }
...terminating worker thread
The output confirms that the function and variable from the library were correctly retrieved.
Let's look at this example, where I'm setting a value in a property store unique to the folder Im running from, and using a local .env file, if there is one. Cache stores work in exactly the same way as the property store examples below.
$ gas-fakes -s "PropertiesService.getScriptProperties().setProperty('hello','world')"I'll see a message like this - in this case i have no .env file in this folder
...using env file in /Users/brucemcpherson/Documents/repos/gas-fakes/t/.env
...etc..
.....PROPERTY store service is using store type FILE as backendNow i can retrieve that value with
$ gas-fakes -s "console.log(PropertiesService.getScriptProperties().getProperty('hello'))"
...
...PROPERTY store service is using store type FILE as backend
worldLet's say that instead of using the default store type, I want to use redis (see - sharing cache and properties between gas-fakes and live apps script), whose details are already defined in some other .env file, I can do this:
$ gas-fakes -s "PropertiesService.getScriptProperties().setProperty('hello','world of redis')" -e ../.env
...PROPERTY store service is using store type UPSTASH as backend
$ gas-fakes -s "console.log(PropertiesService.getScriptProperties().getProperty('hello'))" -e ../.env
...PROPERTY store service is using store type UPSTASH as backend
world of redisA stable scriptId is required to partition property and cache stores. During gas-fakes init, the utility will attempt to discover your scriptId from .clasp.json. If no ID is found in your configuration or in .clasp.json, a random UUID is generated and saved to your .env file as GF_SCRIPT_ID. This ensures that your local stores remain consistent across different sessions.
If you want to share stores between different folders, you need to ensure that the GF_SCRIPT_ID in your .env has the same value. This is especially important if you plan to share property and cache stores with live Apps Script (yes you can!). In this case, you set the GF_SCRIPT_ID in your .env to match the scriptId of the live apps script you want to share these stores with.
$ gas-fakes -s "PropertiesService.getScriptProperties().setProperty('hello','to live apps script')" -e ../.env
....PROPERTY store service is using store type UPSTASH as backend
$ gas-fakes -s "console.log(PropertiesService.getScriptProperties().getProperty('hello'))" -e ../.env
...PROPERTY store service is using store type UPSTASH as backend
to live apps scriptYou can find more info about how to read or update these values directly in live apps script using the Dropin replacement for the property and cache stores in sharing cache and properties between gas-fakes and live apps script
This CLI tool can also be used as the MCP server. Please add the MCP server to settings.json as follows.
"mcpServers": {
"gas-fakes": {
"command": "gas-fakes",
"args": ["mcp"]
}
}When this is used for the Gemini CLI, the following result is obtained.
> /mcp
Configured MCP servers:
🟢 gas-fakes - Ready (2 tools)
Tools:
- create-new-tools
- run-gas-by-gas-fakes
When you want to load your custom tools to gas-fakes MCP, please use the following setting.
"mcpServers": {
"gas-fakes": {
"command": "gas-fakes",
"args": [
"mcp",
"--tools",
"tools.js" // <--- A script file including custom tools.
]
}
}The simple sample script for tools.js is as follows. This tool searches files on Google Drive. The script is Google Apps Script. When this tool is used as the above, a tool searchGoogleDriveFiles will be added to the MCP server.
import { z } from "zod";
const tools = [
{
name: "searchGoogleDriveFiles",
schema: {
description: "Use this to search files by a filename on Google Drive.",
inputSchema: {
filename: z.string().describe("Filename of the search file."),
},
},
func: (object = {}) => {
const { filename } = object;
const files = DriveApp.getFilesByName(filename);
const ar = [];
while (files.hasNext()) {
const file = files.next();
ar.push({ filename: file.getName(), fileId: file.getId() });
}
return ar;
},
},
];- gas fakes intro video
- getting started - how to handle authentication for restricted scopes.
- readme
- gas fakes cli
- ksuite as a back end
- msgraph as a back end
- apps script - a lingua franca for workspace platforms
- Apps Script: A ‘Lingua Franca’ for the Multi-Cloud Era
- running gas-fakes on google cloud run
- running gas-fakes on google kubernetes engine
- running gas-fakes on Amazon AWS lambda
- running gas-fakes on Azure ACA
- Yes – you can run native apps script code on Azure ACA as well!
- Yes – you can run native apps script code on AWS Lambda!
- initial idea and thoughts
- Inside the volatile world of a Google Document
- Apps Script Services on Node – using apps script libraries
- Apps Script environment on Node – more services
- Turning async into synch on Node using workers
- All about Apps Script Enums and how to fake them
- colaborators - additional information for collaborators
- oddities - a collection of oddities uncovered during this project
- named colors
- sandbox
- senstive scopes
- using apps script libraries with gas-fakes
- how libhandler works
- article:using apps script libraries with gas-fakes
- named range identity
- adc and restricted scopes
- push test pull
- sharing cache and properties between gas-fakes and live apps script
- gas-fakes-cli now has built in mcp server and gemini extension
- gas-fakes CLI: Run apps script code directly from your terminal
- How to allow access to sensitive scopes with Application Default Credentials
- Supercharge Your Google Apps Script Caching with GasFlexCache
- Fake-Sandbox for Google Apps Script: Granular controls.
- A Fake-Sandbox for Google Apps Script: Securely Executing Code Generated by Gemini CLI
- Power of Google Apps Script: Building MCP Server Tools for Gemini CLI and Google Antigravity in Google Workspace Automation
- A New Era for Google Apps Script: Unlocking the Future of Google Workspace Automation with Natural Language
- Next-Generation Google Apps Script Development: Leveraging Antigravity and Gemini 3.0
- Modern Google Apps Script Workflow Building on the Cloud
- Bridging the Gap: Seamless Integration for Local Google Apps Script Development
- Next-Level Google Apps Script Development
- Secure and Streamlined Google Apps Script Development with gas-fakes CLI and Gemini CLI Extension
- Secure and Conversational Google Workspace Automation: Integrating Gemini CLI with a gas-fakes MCP Server
- A Fake-Sandbox for Google Apps Script: A Feasibility Study on Securely Executing Code Generated by Gemini CL

