MCP Server wrapping Google Workspace CLI (gws) to expose gws commands as MCP tools over Streamable HTTP transport. Provides tools for Gmail, Drive, Calendar, Sheets, Docs, Slides, Tasks, People, Chat, Classroom, Forms, Keep, Meet, Events, Admin Reports, Model Armor, and Workflows.
The server exposes 15 Google Workspace tools with reasonable naming (verb_resource pattern) and descriptions present for all tools and parameters. However, significant gaps exist: (1) parameter descriptions are generic and lack constraints (no enums for roles, no format specs for IDs); (2) output schemas are not documented, callers cannot predict response structure; (3) error handling is minimal, the run_gws helper returns raw CLI output or error dicts without recovery guidance; (4) no tool annotations (readOnlyHint/destructiveHint) despite having both READ_ONLY and WRITE tools; (5) descriptions are brief (avg ~60 chars) and lack "when to use" context; (6) critical parameters like 'role' in drive_create_permission accept free-form strings instead of enums. The codebase is clean and async-first, but the tool definitions lack LLM-friendly polish.
Create a new file in Google Drive.
Share a Drive file by creating a permission.
Get metadata for a Drive file.
List/search files in Google Drive.
List permissions on a Drive file or shared drive.
Upload a file to Drive with automatic metadata using +upload helper.
Get a specific Gmail message by ID.
Output schemas not documented. Tools like gmail_list_messages, drive_list_files return raw gws CLI JSON, but callers cannot predict field names, types, or pagination structure. LLMs cannot plan downstream calls without knowing what fields to extract.
Parameter 'role' in drive_create_permission lacks enum constraint. Description says 'owner, organizer, fileOrganizer, writer, commenter, reader' but is free-form string, LLMs may hallucinate invalid values like 'admin' or 'editor'. Should be declared as enum.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | B | 70 | <=2025-11-25 | v2 |
Get a Gmail thread by ID.
List all Gmail labels.
Search/list Gmail messages.
List Gmail threads.
Modify labels on a Gmail message (e.g. mark read/unread, archive).
Send an email via Gmail using the +send helper.
Show unread inbox summary (sender, subject, date) using +triage helper.
Watch for new emails and stream them as NDJSON using +watch helper.
Parameter 'type' in drive_create_permission lacks enum constraint. Description says 'user, group, domain, anyone' but is free-form, should be enum. Lack of enum forces LLM to guess or ask user.
No tool annotations (readOnlyHint/destructiveHint/idempotentHint). Tools like gmail_send, drive_create_file, drive_upload are destructive (WRITE risk) but lack destructiveHint annotation. LLMs cannot tell which tools require extra caution without reading descriptions.
Error handling is minimal. run_gws() returns raw exit codes and stderr. No recovery guidance. If gmail_send fails with 'Invalid recipient', the LLM has no instruction on what to try next. Errors should be actionable.
Parameter descriptions are generic. 'Gmail search query (e.g. 'is:unread', 'from:user@example.com').' is helpful but lacks constraints (min/max length, forbidden characters). 'Maximum number of messages to return' lacks bounds (1 - 1000?). LLMs may pass invalid values without formal constraints.
Tool descriptions are brief (avg ~60 chars, baseline for A+ is 50 - 200). Descriptions lack 'when to use' context. E.g., gmail_triage says 'Show unread inbox summary' but does not explain when to call it vs gmail_list_messages. Disambiguation helps LLM choose right tool.
No pagination guidance. gmail_list_messages accepts max_results but no documented page/cursor mechanism. If results exceed max_results, LLM cannot fetch next page. Description should clarify pagination and limits.
gmail_watch returns NDJSON stream but no schema documented. Callers cannot predict message field names or structure. If response is async or long-lived, error handling is unclear.
Parameter 'order_by' in drive_list_files accepts free-form string ('modifiedTime desc' as default) with no enum or format spec. Valid values unknown to LLM (modifiedTime vs modified_time vs lastModifiedTime?). Should be enum: ['modifiedTime desc', 'modifiedTime asc', 'name asc', 'name desc', 'createdTime desc', 'createdTime asc'].