Python MCP server for reading Google Docs, Google Sheets, and Google Chat with structured output and better image handling.
- Reads Google Docs as structured JSON with paragraphs, tables, inline objects, positioned objects, and image metadata.
- Reads Google Sheets values, grid data, formulas, notes, hyperlinks, and chip runs.
- Lists Google Chat spaces, space members, and messages as structured JSON.
- Preserves partial text styling in Sheets cells via
text_runsand anannotated_texthelper field for segments such as strikethrough and underline. - Extracts over-grid sheet images from
Drive export -> XLSX. - Detects in-cell
IMAGE("...")formulas separately from drawing exports.
google_workspace_mcp/
chat.py # Google Chat normalization helpers
cli.py # command-line entrypoint
client.py # Google API auth + HTTP client
common.py # shared constants and parsing helpers
docs.py # Google Docs normalization helpers
server.py # FastMCP server instance
sheets.py # Google Sheets normalization helpers
tools.py # MCP tool definitions
mcp_google_workspace.py # compatibility wrapper for local scripts/config
tests/
pip install "git+https://github.com/NgoQuocViet2001/google-workspace-mcp.git"The most reliable way to run it across platforms is:
python -m google_workspace_mcpIf your Python Scripts directory is already on PATH, this shorter launcher works too:
google-workspace-mcp- In Google Cloud, create an OAuth client ID with application type
Desktop app. - If the published package already includes a bundled OAuth desktop client, just run:
python -m google_workspace_mcp auth loginThat opens the browser OAuth flow directly.
- If the package does not include a bundled OAuth client and no local OAuth client config exists yet, the CLI prompts once for:
Client IDClient Secret
It then saves a reusable desktop-app client config at:
$HOME/.google-workspace-mcp/oauth-client-secret.jsonand opens the browser OAuth flow automatically. After the first successful login, the cached token is stored at:
$HOME/.google-workspace-mcp/oauth-token.jsonpython -m google_workspace_mcp auth status
python -m google_workspace_mcp auth logoutUse python -m google_workspace_mcp ... everywhere below. If google-workspace-mcp is already on PATH, the same commands also work with that shorter launcher.
python -m google_workspace_mcppython -m google_workspace_mcp authpython -m google_workspace_mcp auth loginpython -m google_workspace_mcp auth login --client-secrets C:\path\to\oauth-client-secret.jsonpython -m google_workspace_mcp auth login --client-id <client-id> --client-secret <client-secret>python -m google_workspace_mcp auth login --token-file C:\path\to\oauth-token.jsonpython -m google_workspace_mcp auth statuspython -m google_workspace_mcp auth logout
Use a Google OAuth client ID for Desktop App if the files are private but shared to your personal Google account.
If you want end users to be able to run python -m google_workspace_mcp auth login and jump straight into the browser OAuth flow with no extra setup, publish the package with a bundled desktop-app client at:
google_workspace_mcp/oauth-default-client.json
If no bundled client is shipped, the CLI falls back to prompting once for Client ID and Client Secret, or it can read them from a local JSON file.
- Enable:
- Google Sheets API
- Google Docs API
- Google Drive API
- Google Chat API
- Create an OAuth client ID with application type
Desktop app. - Choose one setup method:
- Easiest: run
python -m google_workspace_mcp auth login, paste theClient IDandClient Secretonce, and let the CLI save them for future logins. - If you prefer files: download the client secret JSON. The downloaded filename is often something like
client_secret_<id>.apps.googleusercontent.com.json.
- Easiest: run
- Optional file-based setup:
$HOME/.google-workspace-mcp/oauth-client-secret.jsonOr set:
$env:GOOGLE_OAUTH_CLIENT_SECRETS_FILE="C:\path\to\oauth-client-secret.json"- Run the one-time browser login flow:
python -m google_workspace_mcp authAfter the first successful login, the server automatically uses the cached OAuth token for private Docs, Sheets, Drive, and Google Chat calls. You do not need to provide a separate API key for that flow.
This stores a refreshable token by default at:
$HOME\.google-workspace-mcp\oauth-token.jsonUse this to inspect the cached token scopes and see which scopes are still missing:
python -m google_workspace_mcp auth statusIf auth status shows only drive.readonly, Sheets URLs that include gid and range can still be read through the server's Drive export fallback. That fallback returns values-oriented output and omits formulas, notes, hyperlinks, and rich text metadata until you re-run python -m google_workspace_mcp auth login with spreadsheets.readonly.
If you need to overwrite the cached token with a specific client secret file and token path, you can also run:
python -m google_workspace_mcp auth login --client-secrets C:\path\to\oauth-client-secret.json --token-file C:\path\to\oauth-token.jsonIf the desktop-app client JSON is already in $HOME/.google-workspace-mcp/, this shorter command also works:
python -m google_workspace_mcp auth loginWhen you log in with --client-secrets, or with --client-id plus --client-secret, the CLI also saves a reusable desktop-app client JSON into $HOME/.google-workspace-mcp/oauth-client-secret.json so future logins can omit the extra flags.
To delete the cached OAuth token later, run:
python -m google_workspace_mcp auth logoutIf you separately configured GOOGLE_OAUTH_ACCESS_TOKEN, remove that environment variable from your shell or MCP config as well.
Use a Google Cloud service account for the most reliable setup.
- Enable:
- Google Sheets API
- Google Docs API
- Google Drive API
- Google Chat API if you plan to call the Chat tools with a user-scoped bearer token
- Create a service account key.
- Share the target Docs/Sheets files with the service account email.
- Set:
$env:GOOGLE_SERVICE_ACCOUNT_FILE="C:\path\to\service-account.json"Suitable for public Google Sheets reads. Not recommended for Docs or Drive export.
$env:GOOGLE_API_KEY="your_api_key"$env:GOOGLE_OAUTH_ACCESS_TOKEN="ya29...."git clone https://github.com/NgoQuocViet2001/google-workspace-mcp.git
cd google-workspace-mcp
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txtgit clone https://github.com/NgoQuocViet2001/google-workspace-mcp.git
cd google-workspace-mcp
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txtpip install "git+https://github.com/NgoQuocViet2001/google-workspace-mcp.git"After installing from GitHub, the most reliable way to run it is:
python -m google_workspace_mcpThis avoids PATH issues when pip installs console scripts into a user-site Scripts directory.
If your Python Scripts directory is already on PATH, the standalone command also works:
google-workspace-mcpcd <path-to-repo>
.venv\Scripts\python.exe mcp_google_workspace.pyOr, if you installed it directly from GitHub:
python -m google_workspace_mcpTo bootstrap OAuth for a private user account:
python -m google_workspace_mcp authIf your google-workspace-mcp launcher is already available on PATH, the equivalent shorter command is:
google-workspace-mcp auth loginTo inspect the current auth setup:
python -m google_workspace_mcp auth statusTo remove the cached OAuth login:
python -m google_workspace_mcp auth logout{
"mcpServers": {
"google-workspace": {
"command": "<path-to-repo>/.venv/Scripts/python.exe",
"args": ["<path-to-repo>/mcp_google_workspace.py"],
"env": {
"GOOGLE_OAUTH_CLIENT_SECRETS_FILE": "C:/path/to/oauth-client-secret.json",
"GOOGLE_OAUTH_TOKEN_FILE": "C:/path/to/oauth-token.json"
}
}
}
}For public Sheets only, replace the env block with:
{
"GOOGLE_API_KEY": "your_api_key"
}If you installed the package directly from GitHub into an environment on your PATH, you can also use:
{
"mcpServers": {
"google-workspace": {
"command": "google-workspace-mcp",
"env": {
"GOOGLE_OAUTH_CLIENT_SECRETS_FILE": "C:/path/to/oauth-client-secret.json",
"GOOGLE_OAUTH_TOKEN_FILE": "C:/path/to/oauth-token.json"
}
}
}
}diagnose_google_authresolve_google_filelist_google_chat_spacesget_google_chat_spaceget_google_chat_messageread_google_chat_messagesread_google_chat_threadlist_google_chat_membershipsread_sheet_valuesread_sheet_gridget_sheet_rowsearch_sheetsheet_to_jsoninspect_sheet_imagesread_google_docdownload_google_doc_imagesexport_google_file
Replace placeholders such as <spreadsheet-id>, <sheet-name>, and <output-dir> with your own values.
Google Sheets URLs with gid and range are resolved automatically. If the caller omits the sheet prefix, the server uses the tab identified by gid.
get_sheet_row(
"<spreadsheet-id>",
"<sheet-name>",
42,
1
)
read_sheet_values also accepts row-style input such as <sheet-name>!42:42 and normalizes it to a valid full-row A1 range automatically.
read_sheet_values(
"https://docs.google.com/spreadsheets/d/<spreadsheet-id>/edit?gid=<gid>#gid=<gid>&range=38:38"
)
When the cached OAuth token has drive.readonly but not spreadsheets.readonly, this still succeeds by exporting the addressed tab/range as CSV behind the scenes. The response includes source: "drive_export_csv_fallback" and an auth_warning so callers know richer grid metadata is unavailable.
read_sheet_grid(
"<spreadsheet-id>",
"<sheet-name>!A1:Z200"
)
For cells with partial formatting, read_sheet_grid() now includes:
text_runs: structured offsets plus style flags from Google Sheetsannotated_text: a plain-text helper string such as[[STRIKE]]old[[/STRIKE]] new
search_sheet(
"<spreadsheet-id>",
"login"
)
If you pass a Sheets URL with gid, search_sheet() searches only that tab by default instead of scanning the full workbook.
list_google_chat_spaces()
read_google_chat_messages() accepts either a resource name like spaces/AAAA... or a Chat UI URL that contains the space id.
read_google_chat_messages(
"spaces/AAAA1234567",
50,
null,
null,
"DESC",
false
)
read_google_chat_thread() accepts either a thread resource like spaces/<space>/threads/<thread> or a Chat UI URL like https://chat.google.com/room/<space>/<thread>/<message>.
read_google_chat_thread(
"https://chat.google.com/room/AAQAyxdRoZo/jVIpmenXnO0/WNSdv6IyQf0?cls=10"
)
When the URL includes a message ID, the response includes both:
linked_message: the exact message referenced by the linkroot_message: the first message in the thread
If Google Chat doesn't expose an API message resource that matches the URL token, the response sets linked_message to null and explains the limitation in linked_message_lookup_warning.
list_google_chat_memberships(
"spaces/AAAA1234567"
)
sheet_to_json(
"<spreadsheet-id>",
"<sheet-name>",
1
)
inspect_sheet_images(
"<spreadsheet-id>",
"<sheet-name>",
"C:/path/to/output/sheet-images"
)
read_google_doc(
"https://docs.google.com/document/d/<doc-id>/edit",
null,
false,
null
)
download_google_doc_images(
"https://docs.google.com/document/d/<doc-id>/edit",
"C:/path/to/output/doc-images",
null
)
- Google Docs image metadata is available directly through the Docs API, so document extraction is strong.
- Google Sheets does not expose over-grid images as cleanly as cell data, so this server uses XLSX export to recover them.
- Google Chat reads require OAuth scopes such as
chat.spaces.readonly,chat.messages.readonly, andchat.memberships.readonly. If your cached token is older, rerunpython -m google_workspace_mcp auth login. - Google Chat API requests also require a configured Chat app in the same Google Cloud project. In
Google Chat API > Configuration, fill in at leastApp name,Avatar URL, andDescription, then save. - Google Chat room URLs always expose the space ID, and often work for thread reads, but the final URL token isn't guaranteed to be a
spaces/{space}/messages/{message}API resource name. In those cases the server can still return the thread and root message, but not reliably resolve the exact linked reply through the Chat API alone. - Google Chat private user conversations are most reliable with OAuth user credentials. A plain service account usually needs a properly configured Chat app flow to access Chat resources.
- In-cell
IMAGE("...")formulas are detected separately from exported drawing images. - Private files shared to your user account should use the OAuth desktop client flow.
- Private files shared to a robot identity should use a service account.
- An API key is only suitable for public Sheets and can't read Google Chat.
drive.readonlyalone is enough for the Sheets URL fallback described above, but Google Docs still requiredocuments.readonly.