@cl0ud95/google-workspace-oauth-mcp
v1.1.1
Published
MCP server for Google Workspace (Drive, Sheets, Gmail, Calendar, Tasks) with OAuth authentication
Readme
Google Workspace OAuth MCP Server
MCP server providing Google Drive, Sheets, Gmail, Calendar, and Tasks access via OAuth authentication. OAuth credentials are managed externally (control plane or file-based) — the MCP server only reads tokens and makes API calls.
Version
4.0.0 — OAuth-only server. Split from original dual-mode server. Removed service account authentication.
Architecture
Control Plane Token Mode
flowchart TB
subgraph CP["OAuth Control Plane"]
AuthAPI["/internal/google/auth-url<br/>/internal/google/tokens"]
Onboard["/onboarding/google/callback"]
DB["client_mcp_configs<br/>(encrypted tokens)"]
AuthAPI --> DB
Onboard --> DB
end
subgraph Container["Agent Container"]
MCP["MCP Server (this package)"]
OAuth2["google-auth-library<br/>OAuth2Client"]
APIs["googleapis<br/>(Drive/Sheets/Gmail/Calendar/Tasks)"]
MCP -->|"fetch tokens<br/>request auth URL"| AuthAPI
MCP --> OAuth2
MCP --> APIs
APIs -->|"API calls"| Google["Google Workspace APIs"]
OAuth2 -->|"refresh token<br/>rotation"| AuthAPI
end
User["User Browser"] -->|"consent"| Onboard
style CP fill:#e1f5fe
style Container fill:#f3e5f5
style DB fill:#fff9c4
style Google fill:#e8f5e9File-Based Token Mode
flowchart TB
subgraph Container["Agent Container"]
MCP["MCP Server (this package)"]
OAuth2["google-auth-library<br/>OAuth2Client"]
APIs["googleapis<br/>(Drive/Sheets/Gmail/Calendar/Tasks)"]
MCP -->|"read/write tokens"| TokenFile["google-tokens.json<br/>(local file)"]
MCP --> OAuth2
MCP --> APIs
APIs -->|"API calls"| Google["Google Workspace APIs"]
OAuth2 -->|"refresh updated tokens"| TokenFile
end
style Container fill:#f3e5f5
style Google fill:#e8f5e9
style TokenFile fill:#fff9c4How It Works — Control Plane Mode
- Agent calls MCP tool (e.g.,
drive_list_files) - MCP checks scopes — calls control plane to fetch tokens, verifies the token has the required scopes
- Has scopes — refreshes access token if expired, executes API call
- Missing scopes — requests auth URL from control plane, returns
AUTH_REQUIREDwith URL and missing scope info - Agent sends URL to user — user taps, Google shows only new permissions (incremental consent)
- Control plane callback — exchanges code, stores tokens encrypted in
client_mcp_configs - Agent retries — MCP fetches fresh tokens from control plane, succeeds
How It Works — File-Based Token Mode
- Agent calls MCP tool
- MCP reads
GOOGLE_TOKEN_PATH— loads tokens from local JSON file - No file or empty — returns
AUTH_REQUIREDwith the expected file path and required scopes - Has tokens — creates
OAuth2Client, refreshes access token if expired (using refresh token) - Token refresh — updated access token and expiry are written back to the file automatically
- Refresh fails — tokens are cleared, returns
AUTH_REQUIRED
The tokens file must contain:
{
"access_token": "",
"refresh_token": "1//0abc...",
"expires_at": 0,
"scope": "https://www.googleapis.com/auth/drive ..."
}access_token can be empty and expires_at can be 0 — the server will immediately refresh using the refresh_token.
Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| GOOGLE_CLIENT_ID | Yes | OAuth client ID from GCP Console |
| GOOGLE_CLIENT_SECRET | Yes | OAuth client secret from GCP Console |
| GOOGLE_TOKEN_PATH | Conditional | Path to local google-tokens.json file (required unless GOOGLE_AUTH_URL is set) |
| GOOGLE_AUTH_URL | Conditional | Control plane base URL (required unless GOOGLE_TOKEN_PATH is set) |
| ENCRYPTION_KEY | Conditional | Shared secret for control plane API auth (required when using GOOGLE_AUTH_URL) |
| CLIENT_ID | Conditional | Client identifier for control plane (required when using GOOGLE_AUTH_URL) |
| AGENT_CONFIG_ID | Conditional | Agent identifier for control plane (required when using GOOGLE_AUTH_URL) |
| GOOGLE_ROOT_FOLDER_ID | No | Google Drive folder ID that scopes Drive operations |
| GOOGLE_SERVICES | No | Comma-separated enabled services (default: drive,sheets,gmail,calendar,tasks) |
| GOOGLE_READONLY | No | If true, only read tools for Drive/Sheets (default: false) |
Scope-Aware Auth
Each tool declares required OAuth scopes. Before executing, the MCP verifies the stored token covers those scopes. If not:
{
"error": "AUTH_REQUIRED",
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth?...",
"missing_scopes": ["https://www.googleapis.com/auth/gmail.compose"],
"current_scopes": ["https://www.googleapis.com/auth/drive"],
"message": "Google Workspace access required. Missing scopes: ..."
}Scopes are requested for all configured services upfront (one consent screen). Re-auth is only required if GOOGLE_SERVICES is expanded to include a new service.
check_google_auth Tool
Proactively check connection status without triggering an error:
Input: { requested_scopes: "drive,sheets" }
Output: {
"connected": true,
"current_scopes": ["https://www.googleapis.com/auth/drive", ...],
"missing_scopes": [],
"auth_url": null
}Token Management
Control Plane Mode
- Fetch: MCP calls
GET /internal/google/tokenson first API call - Refresh: Uses
google-auth-library'sOAuth2Clientwith 60-second proactive buffer - Persist: Only persists back to control plane when refresh token rotates (rare)
- Re-auth: After onboarding, MCP re-fetches from control plane — no container restart needed
File-Based Mode
- Fetch: MCP reads
GOOGLE_TOKEN_PATHon first API call - Refresh: Uses
google-auth-library'sOAuth2Clientwith 60-second proactive buffer - Persist: Updated tokens (access token, expiry) are written back to the file on every refresh
- Re-auth: If the file is deleted or refresh fails, returns
AUTH_REQUIREDwith the expected file path
Tools (52 total)
Meta
| Tool | Description |
|------|-------------|
| check_google_auth | Check connection status, missing scopes, get auth URL |
Drive (16 tools)
| Tool | R/W | Description |
|------|-----|-------------|
| drive_list_files | R | List files in folder (paginated) |
| drive_list_folders | R | List only folders in folder |
| drive_get_file | R | Get file metadata |
| drive_read_file | R | Read text file content (truncates at 1MB) |
| drive_download | R | Get download URL for binary files |
| drive_search | R | Search files by name in root tree |
| drive_tree | R | Get folder tree structure |
| drive_create_folder | W | Create folder |
| drive_create_file | W | Create text file |
| drive_update_file | W | Update file content |
| drive_move_file | W | Move file to different folder |
| drive_rename_file | W | Rename file/folder |
| drive_delete_file | W | Trash file/folder |
| drive_share_file | W | Set link sharing (private/anyone/anyone_with_link) |
| drive_add_collaborator | W | Add user as collaborator |
| drive_remove_collaborator | W | Remove collaborator |
| drive_get_permissions | R | Get file permissions and collaborators |
Sheets (13 tools)
| Tool | R/W | Description |
|------|-----|-------------|
| sheets_list | R | List all spreadsheets in root folder |
| sheets_get_info | R | Get spreadsheet metadata and sheets |
| sheets_get_sheet | R | Get specific sheet/tab metadata |
| sheets_read_cell | R | Read single cell |
| sheets_read_range | R | Read range (truncates at 10k rows) |
| sheets_read_all | R | Read entire sheet |
| sheets_write_cell | W | Write single cell |
| sheets_write_range | W | Write 2D array to range |
| sheets_append_row | W | Append row to end of sheet |
| sheets_clear_range | W | Clear range values |
| sheets_create_sheet | W | Create new sheet/tab |
| sheets_delete_sheet | W | Delete sheet/tab |
| sheets_create_spreadsheet | W | Create new spreadsheet file (optional parent folder, first sheet name) |
Gmail (8 tools)
| Tool | R/W | Description |
|------|-----|-------------|
| gmail_search_messages | R | Search messages with query |
| gmail_read_message | R | Get full message with decoded body and attachment metadata |
| gmail_read_thread | R | Get all messages in thread with attachment metadata |
| gmail_send_message | W | Send email (HTML or plain text, optional attachments) |
| gmail_create_draft | W | Create email draft (optional attachments) |
| gmail_modify_labels | W | Add/remove labels on message |
| gmail_list_labels | R | List all labels |
| gmail_get_attachment | R | Download attachment content by messageId and attachmentId |
Calendar (8 tools)
| Tool | R/W | Description |
|------|-----|-------------|
| gcal_list_calendars | R | List all calendars |
| gcal_list_events | R | List events in calendar |
| gcal_get_event | R | Get single event details |
| gcal_create_event | W | Create new event |
| gcal_update_event | W | Update existing event |
| gcal_delete_event | W | Delete event |
| gcal_respond_to_event | W | Accept/decline/tentative response |
Tasks (8 tools)
| Tool | R/W | Description |
|------|-----|-------------|
| gtasks_list_tasklists | R | List all task lists |
| gtasks_get_tasklist | R | Get a specific task list |
| gtasks_list_tasks | R | List tasks in a task list (filter by completion/due date) |
| gtasks_get_task | R | Get a specific task |
| gtasks_create_task | W | Create a new task |
| gtasks_update_task | W | Update task (title, notes, status, due date) |
| gtasks_delete_task | W | Delete a task |
| gtasks_clear_tasks | W | Clear all completed tasks from a list |
Service Filtering
Only register tools the agent needs — saves context tokens:
"GOOGLE_SERVICES": "sheets" // Only Sheets tools
"GOOGLE_SERVICES": "drive,sheets" // Drive + Sheets
"GOOGLE_SERVICES": "drive,sheets,gmail" // Drive + Sheets + GmailOAuth Scopes
| Service | Scope URLs |
|---------|-----------|
| Drive | https://www.googleapis.com/auth/drive |
| Sheets | https://www.googleapis.com/auth/spreadsheets |
| Gmail | https://www.googleapis.com/auth/gmail.compose, https://www.googleapis.com/auth/gmail.modify |
| Calendar | https://www.googleapis.com/auth/calendar |
| Tasks | https://www.googleapis.com/auth/tasks |
Installation
npx -y @cl0ud95/google-workspace-oauth-mcpDevelopment
npm install
npm run build # Compile TypeScript
npm run dev # Run with tsx
npm run typecheck # Type check onlyLicense
MIT
