Policy reference

Every field in the policy file, CLI flag, and environment variable, explained.

The policy file is a JSON file with two top-level keys: "server" for global settings and "services" for per-service configuration.

"server" object

FieldTypeDefaultDescription
read_onlybooleanfalseBlock all write operations across all services
max_request_bytesinteger16777216 (16 MB)Maximum request body size in bytes
rate_limit_rpmintegerunlimitedMax requests per minute per client IP (HTTP only)
allowed_originslist of strings[] (localhost only)Allowed HTTP Origin hostnames
credentials_filestring--Path to Google credentials JSON
project_idstring--Google Cloud project ID for quota and billing
{
  "server": {
    "read_only": false,
    "rate_limit_rpm": 120,
    "allowed_origins": ["internal.corp.com"],
    "credentials_file": "/path/to/credentials.json",
    "project_id": "my-project-123456"
  }
}

"services" array

Only services listed here are exposed as MCP tools. Everything else is denied.

FieldTypeDefaultDescription
namestringrequiredService alias (see table below)
read_onlybooleaninherits from "server"Override global read_only for this service
denied_methodslist of strings[]Methods to block. Suffix matching: "messages.send" matches "users.messages.send"
allowed_labelslist of stringsall labelsGmail only: restrict message access to specific labels
constraintslist of objects[]Parameter-level access controls (see below)
rate_limitsobject--Per-service rate limiting overrides

Available services

Any Google Workspace API discoverable through the Google Discovery Service can be used. Common aliases:

AliasGoogle APIDescription
driveGoogle Drive API v3Files, folders, permissions, comments
gmailGmail API v1Messages, threads, labels, drafts, contacts
calendarGoogle Calendar API v3Events, calendars, settings
sheetsGoogle Sheets API v4Spreadsheets, values, charts
docsGoogle Docs API v1Documents, content, styles
slidesGoogle Slides API v1Presentations, pages, elements
peoplePeople API v1Contacts, profiles, directory
adminAdmin SDK Directory APIUsers, groups, organizational units
chatGoogle Chat API v1Spaces, messages, memberships

Gmail label policy

The allowed_labels field restricts which Gmail labels the agent can access. When set, the server injects a label filter into search queries and validates message access against the allowed list.

{
  "services": [
    {
      "name": "gmail",
      "allowed_labels": ["INBOX", "SENT", "IMPORTANT"],
      "denied_methods": ["messages.delete", "messages.trash"]
    }
  ]
}

Per-service rate limits

Override the global rate limit for specific services to respect Google API quotas:

{
  "services": [
    {
      "name": "sheets",
      "rate_limits": {
        "requests_per_minute": 60
      }
    }
  ]
}

"constraints" -- Parameter-level access controls

Constraints let you restrict which values a parameter can take. Each constraint specifies a parameter name, a list of allowed values, and an access level.

FieldTypeDefaultDescription
paramstringrequiredParameter name (from the Google API Discovery Document)
valueslist of stringsrequiredAllowed values for this parameter
accessstringread-writeread-only or read-write
locationstringauto-detectbody for request body fields, omit for path/query params

Constraints are generic -- the same mechanism works for any service and any parameter. The server validates constraint values against the actual API call at runtime.

How constraints work

  • Path/query parameters: the value provided by the agent must be in the constraint's values list. If the constraint is read-only, write operations are denied.
  • Body parameters (with "location": "body"): on write operations, the body field must be present and its values must all be in the allowed list. On read/list operations, the server injects a query filter to restrict results.
  • Parent ancestry: For Drive folder constraints, the server recursively checks parent folders. A file in a subfolder of an allowed folder inherits access.
  • Multiple constraints on the same parameter are merged -- all values across constraints are allowed, and each value inherits the access level of its constraint.

Examples

Drive -- restrict to specific folders:

{
  "services": [
    {
      "name": "drive",
      "constraints": [
        { "param": "parents", "values": ["folder-abc"], "access": "read-only", "location": "body" },
        { "param": "parents", "values": ["folder-xyz"], "access": "read-write", "location": "body" }
      ]
    }
  ]
}

Calendar -- restrict to specific calendars:

{
  "services": [
    {
      "name": "calendar",
      "constraints": [
        { "param": "calendarId", "values": ["primary"], "access": "read-write" },
        { "param": "calendarId", "values": ["holidays@group.calendar.google.com"], "access": "read-only" }
      ]
    }
  ]
}

Sheets -- restrict to a specific spreadsheet:

{
  "services": [
    {
      "name": "sheets",
      "constraints": [
        { "param": "spreadsheetId", "values": ["1BxiMVs0XRA..."], "access": "read-only" }
      ]
    }
  ]
}

Slides -- protect templates:

{
  "services": [
    {
      "name": "slides",
      "constraints": [
        { "param": "presentationId", "values": ["template-id-123"], "access": "read-only" }
      ]
    }
  ]
}

Gmail -- block dangerous methods:

{
  "services": [
    {
      "name": "gmail",
      "allowed_labels": ["INBOX", "SENT"],
      "denied_methods": [
        "messages.delete", "messages.trash", "messages.batchDelete",
        "settings.updateAutoForwarding",
        "settings.delegates.create",
        "settings.forwardingAddresses.create"
      ]
    }
  ]
}

Evaluation order

Every request passes through these checks in order. All checks must pass -- the first failure stops evaluation and denies the request.

  1. Service allow-list -- Is the service listed in "services"? Unlisted services are denied entirely.
  2. Read-only -- Is the service (or server) read-only? If yes, non-GET methods are denied.
  3. Denied methods -- Is the method in the denied_methods list? Uses suffix matching: "messages.send" in the denylist matches API method "users.messages.send".
  4. Label policy -- For Gmail, is the message's label in allowed_labels?
  5. Constraints -- Do the parameter values match the allowed list? Is the access level sufficient for the operation?

There is no override mechanism -- a method denied at step 2 cannot be re-allowed at step 5. Each layer can only narrow access, never widen it.

Every denial includes a Fix: hint with the exact JSON snippet to add or change in the policy file.

Note: This policy engine is specific to this MCP server. The upstream google-workspace crate (gws CLI) has no policy or permission model -- it relies solely on OAuth scopes.

CLI flags

Server

FlagDescription
--policy <path>Path to a JSON policy file
--services <list>Comma-separated service names (no constraints)
--http <addr:port>Run as HTTP server instead of stdio
--eager-toolsLoad all helper tools at startup instead of lazy discovery
--prompts-dir <path>Directory of Markdown prompt files for MCP prompts
--audit-log <path>Write JSONL audit log of all API calls

Policy tools

FlagDescription
initGuided setup: check auth, pick services, folders, labels, calendars
--template <name>Use a preset: analyst, assistant, admin-readonly. Use --template list for details
check-policy <path>Validate a policy file and show security warnings
--verifyWith check-policy: test credentials against Google APIs

Templates

TemplateServicesDescription
analystDrive (RO), Sheets (RO), Docs (RO), Gmail (send-only)Safe for data analysis
assistantDrive, Gmail (safe), Calendar (primary), Sheets, Docs (RO)General-purpose assistant
admin-readonlyAll servicesGlobal read-only, safe for auditing

Live policy reload

When running with --http and --policy, the server reloads the policy file on SIGHUP without restarting:

kill -HUP $(pidof mcp-google-workspace)

Failed reloads log an error and keep the current policy. Not available in stdio mode.

Audit log

The --audit-log flag writes a JSONL file with one entry per API call:

{"timestamp":"1718745600.000","action":"allowed","service":"drive","resource":"files","method":"list","http_method":"GET","tool":"gws_drive_list","status":200,"duration_ms":142}
{"timestamp":"1718745601.000","action":"denied","service":"docs","resource":"documents","method":"create","reason":"Service 'docs' is read-only"}

Request explanation

Every write operation (POST, PUT, PATCH, DELETE) includes an _explanation field in the response's structuredContent:

{
  "structuredContent": {
    "_explanation": "Create drive/files.create (POST): name=\"report.pdf\", in folder folder-xyz",
    "id": "...",
    "name": "report.pdf"
  }
}

This helps agents surface what happened to the user in plain language.

Environment variables

Policy JSON fields take precedence where both are available.

Credentials

VariableDescription
GOOGLE_WORKSPACE_CLI_TOKENRaw OAuth2 access token (highest priority)
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILEPath to credentials JSON
GOOGLE_APPLICATION_CREDENTIALSPath to credentials JSON (ADC standard)

Quota and project

VariableDescription
GOOGLE_WORKSPACE_PROJECT_IDGoogle Cloud project ID. Overridden by project_id in policy.

Observability

VariableDescription
RUST_LOGLog level filter (e.g., info, debug, mcp_google_workspace=debug)
OTEL_EXPORTER_OTLP_ENDPOINTOTLP endpoint for trace export (e.g., http://localhost:4318)

Edit this page on GitHub