Google Workspace Directory MCP
Provides tools for querying Google Workspace directory users, including status, aliases, and summaries via the Admin SDK Directory API (read-only).
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Google Workspace Directory MCPIs lpena@acme.com still active in the directory?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Google Workspace Directory MCP
Production-oriented, read-only MCP service for narrowly scoped Google Workspace user lookups. It uses Python, FastMCP Streamable HTTP, the Google Admin SDK Directory API, a dedicated service-account JSON credential, Domain-Wide Delegation (DWD), and one fixed delegated-admin subject from server configuration.
The service performs only users.get and users.list. It cannot create, update, suspend, archive, rename, delete, or otherwise modify users.
Architecture and threat boundary
MCP client
-> external TLS and human authentication at Nginx Proxy Manager
-> dedicated ingress network + gateway secret and verified identity headers
-> FastMCP /mcp on 0.0.0.0:8000
-> fixed-subject DWD credential provider
-> Google Admin SDK Directory API (read-only users scope)The external gateway authenticates the human and must inject a shared gateway secret plus a verified caller identity. The application verifies both, authorizes the identity, and includes it with a generated request ID in every tool audit event. The shared secret proves the request came through the trusted ingress path; it does not identify an individual and is not a replacement for network isolation or external TLS. NPM must remove client-supplied copies of both headers before injecting its own values.
The application binds to 0.0.0.0:8000 inside the container so Docker and NPM can reach it. Compose publishes only 127.0.0.1:8000:8000 on the host. The Compose service uses a dedicated external Docker network named google-mcp-ingress; attach only NPM and this service to that network. Do not use the general-purpose proxy network.
The MCP process validates input and the allowed email domains, constructs bounded Google queries from plain search terms, limits search output, requests partial response fields, filters cross-domain aliases, removes control/format characters from directory text, and returns narrow stable schemas. Directory text is untrusted data and is explicitly marked as such in MCP server/tool instructions; clients must not treat names, aliases, paths, or queries as instructions. This is a trust-boundary control, not a substitute for the MCP client's system-level prompt-injection defenses.
DWD is powerful: Google authorizes the OAuth client and scopes, but does not enforce this application's fixed-subject choice. A holder of the service-account private key can write different code that chooses another subject permitted by DWD. This service fixes GOOGLE_DELEGATED_ADMIN in configuration and never accepts the subject as a tool argument, but that is an application control rather than a Google-enforced subject restriction.
Related MCP server: gwsadm-mcp
Phase-one tools
google_user_status(email)google_user_search(query, limit=10);queryis a plain name/email fragment, hard maximum20google_user_aliases(email)google_user_summary(email)
Every explicit email argument must belong to one of GOOGLE_ALLOWED_DOMAINS, compared case-insensitively. Returned alias lists contain only those domains. Secondary Workspace domains must be listed explicitly. The service never follows an alias into another domain.
Planned, not implemented:
google_user_groups(email)requires the additional scopehttps://www.googleapis.com/auth/admin.directory.group.readonly.
No group scope or group API operation is present in phase one.
Google prerequisites
These are manual Google administration steps. This repository does not create cloud resources or credentials.
Create a dedicated Google Cloud project for this workload.
Enable Admin SDK API (
admin.googleapis.com). No other Google API is required by phase one.Create a dedicated service account and enable Domain-Wide Delegation for it.
Create or select a dedicated Workspace delegated-admin user. A narrowly scoped custom admin role should grant:
Admin API > Users > Read (
USERS_RETRIEVE)Admin API > Organizational Units > Read (
ORGANIZATION_UNITS_RETRIEVE)
Assign that role across every OU the service is intended to query. Do not use a daily super-admin account.
In Admin console, open Security > Access and data control > API controls > Manage Domain Wide Delegation. Add the service account's numeric OAuth client ID, not its email address.
Authorize exactly this phase-one scope:
https://www.googleapis.com/auth/admin.directory.user.readonlyCreate a JSON key only if a keyless deployment method is not currently available. Move it immediately to a root/deployment-owner controlled directory outside this repository, set host permissions such as
chmod 600, restrict directory traversal, and document an owner and rotation schedule. Revoke the old key after a tested rotation.
The Compose secrets mechanism mounts the host file read-only but does not provide encryption at rest for that source file. Host storage protections, access control, backup handling, incident response, and rotation remain necessary. Never commit, email, paste into logs, or bake the key into an image.
Configuration
Variable | Required | Meaning |
| yes | Absolute in-container path to the mounted JSON credential |
| yes | Fixed delegated Workspace admin subject |
| yes in production | Explicit Directory customer; |
| yes | Comma-separated Workspace domains accepted for users and aliases |
| no | Must be explicitly |
| yes in production | Random shared secret from the trusted gateway; never a tool argument or log value |
| yes in production | Comma-separated authorized human identities |
| no | Header name; defaults to |
| no | Header name; defaults to |
| no | Optional caller-domain allowlist; otherwise uses |
| no | Listen address; defaults to |
| no | Listen port; defaults to |
| no |
|
| no | HMAC-pseudonymize targets when true |
| required for hashing | At least 32 characters; also pseudonymizes callers when set |
| no | Defaults to |
| no | Defaults to |
| no | Defaults to |
| no | Defaults to |
Normal startup validates configuration and the credential file path and then constructs delegated credentials. It fails fast with a sanitized error if configuration or credential initialization fails. Imports and unit tests do not require credentials.
Build and run
cd google-mcp
cp .env.example .env
chmod 600 .env
# Edit .env; the host credential path must remain outside this repository.
docker compose config
docker compose build
docker compose up -dThe local endpoint is http://127.0.0.1:8000/mcp; NPM should use http://google-mcp:8000/mcp over the dedicated ingress network. Inspect startup without exposing secrets:
docker compose ps
docker compose logs --tail=100 google-mcpNPM is outside this repository and has not been modified. Add the following network to its Compose project, attach app to it, and create the network before starting both projects:
services:
app:
networks:
- proxy
- google-mcp-ingress
networks:
google-mcp-ingress:
external: true
name: google-mcp-ingressThen create an authenticated NPM Proxy Host forwarding to hostname google-mcp, port 8000, and path /mcp. Streamable HTTP requires forwarding both POST and GET, preserving the /mcp path without rewriting, disabling response buffering, and using sufficiently long read/send timeouts. Example NPM advanced configuration, using placeholders only:
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_set_header Connection "";
proxy_set_header X-MCP-Gateway-Secret "REPLACE_WITH_SECRET_FROM_NPM_SECRET_STORE";
proxy_set_header X-Authenticated-User $remote_user;NPM must overwrite these headers, not pass through client values. If the selected NPM authentication mechanism does not populate $remote_user, use an SSO/authentication proxy that supplies a verified identity header; do not treat the shared gateway secret as individual caller identity. Do not enable permissive CORS.
If the dedicated network does not exist yet, create it before starting either Compose project:
docker network create google-mcp-ingressStop and remove the container/network while retaining the external credential file:
docker compose downAn MCP client example is in examples/mcp-client.json. Its URL, hostname, and token are placeholders. Adapt the shape to the specific client and authentication gateway.
Tests
Unit tests mock the Directory API and never contact Google:
cd google-mcp
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements-dev.txt
pytest -qThe containerized test path avoids host Python dependency assumptions:
docker build --target test -t google-mcp:test .
docker run --rm --user "$(id -u):$(id -g)" --read-only --tmpfs /tmp:size=16m \
-v "$PWD/tests:/app/tests:ro" \
google-mcp:test pytest -q -p no:cacheproviderThe live smoke script runs only when explicitly invoked. Use fictional variables below as placeholders and set real test addresses only in the shell, never in files:
export GOOGLE_TEST_USER='known-active-user@example.test'
export GOOGLE_TEST_MISSING_USER='known-missing-user@example.test' # optional
export GOOGLE_TEST_GATEWAY_SECRET='set-only in the shell; never in a file' # required outside test mode
export GOOGLE_TEST_CALLER='agent1@example.org' # required outside test mode
python tests/smoke_mcp.py http://127.0.0.1:8000/mcpIt confirms the exact phase-one tool list, requires the known user to return ACTIVE, optionally requires the missing user to return NOT_FOUND, and prints only states—not complete user records.
Stable response schemas
google_user_status returns exactly these state fields. A genuine Directory API 404 is the only NOT_FOUND condition. ARCHIVED takes precedence over SUSPENDED; all other existing users are ACTIVE. Google's epoch/sentinel last-login value becomes null plus never_logged_in: true. Optional fields remain present as null when disabled. Admin flags default to null unless explicitly exposed; 2SV, last-login, and OU fields default to exposed.
{
"email": "alex.rivera@example.test",
"state": "ACTIVE",
"suspended": false,
"archived": false,
"last_login_time": "2026-08-01T13:45:00.000Z",
"never_logged_in": false,
"org_unit_path": "/Staff/Campus-A",
"is_admin": null,
"is_delegated_admin": null,
"is_enrolled_in_2sv": true,
"is_enforced_in_2sv": true
}For NOT_FOUND, boolean fields are false, nullable fields are null, and never_logged_in is false because no account exists from which to infer login history.
google_user_aliases:
{
"email": "alex.rivera@example.test",
"state": "ACTIVE",
"primary_email": "alex.rivera@example.test",
"aliases": ["a.rivera@example.test"],
"non_editable_aliases": ["alex@example.test"]
}google_user_summary includes all status fields plus requested_email, display_name, given_name, family_name, aliases, and non_editable_aliases. It uses one users.get call.
google_user_search accepts a plain human-entered fragment, not Google Directory query syntax. It safely constructs an email-prefix/name-prefix query or an exact allowed-domain email query.
google_user_search:
{
"query": "Alex Rivera",
"limit": 10,
"count": 1,
"truncated": false,
"next_page_available": false,
"users": [
{
"email": "alex.rivera@example.test",
"display_name": "Alex Rivera",
"state": "ACTIVE",
"suspended": false,
"archived": false,
"last_login_time": "2026-08-01T13:45:00.000Z",
"never_logged_in": false,
"org_unit_path": "/Staff/Campus-A"
}
]
}The configured customer ID is always used. Results outside the allowed domains are omitted and make truncated true. The upstream page token is never exposed; callers should narrow the term when truncated or next_page_available is true. Terms must be 3..128 characters and contain no control/format characters or raw query syntax. Limits outside 1..20 are rejected, and only one upstream page is requested.
Logging and failure behavior
Each tool call emits one structured JSON audit event containing UTC timestamp, generated request ID, verified caller identity (or HMAC pseudonym), tool, masked or HMAC-pseudonymized target, result state/count, latency, and sanitized error category. The service does not log gateway secrets, access tokens, credential contents or paths, private keys, complete Google records, raw prompts, aliases, names, phone/profile data, or raw Google error bodies.
Only HTTP 404 maps to NOT_FOUND. HTTP 401/403 become AUTHORIZATION; 429 and eligible 5xx (500, 502, 503, 504) receive at most four total attempts with exponential backoff and jitter. Timeouts and transient transport failures are also bounded. Other malformed or upstream failures remain explicit sanitized errors.
Troubleshooting
invalid_grant: verify the delegated subject exists, has not been suspended, is in the same Workspace tenant, and the server clock is synchronized with NTP. Also verify the credential belongs to the DWD-enabled service account.unauthorized_client: use the service account's numeric OAuth client ID in DWD and authorize the exact scope shown above. DWD changes can take time to propagate.403/AUTHORIZATION: verify Users Read and Organizational Units Read privileges, OU assignment scope, API access controls, the delegated subject, and the Admin SDK API. A valid key alone is insufficient.Missing scope: compare the DWD entry character-for-character with
https://www.googleapis.com/auth/admin.directory.user.readonly. Phase one intentionally requests no group, Drive, Gmail, Calendar, role-management, or security-management scopes.Wrong delegated subject: correct
GOOGLE_DELEGATED_ADMIN; it must be the dedicated delegated admin whose role covers the queried OUs. The MCP caller cannot override it.Clock skew: synchronize the Docker host clock. Signed JWT assertions are time-sensitive.
NOT_FOUNDunexpectedly: confirm the requested email uses the configured allowed domain and is a current, non-deleted Directory user. Authorization and rate-limit failures never becomeNOT_FOUND.
Emergency revocation
If the gateway, service account, or delegated identity is suspected to be compromised:
Disable or remove the NPM route.
Stop the MCP container.
Remove the DWD client entry if compromise is suspected.
Disable or delete the service-account key.
Disable the delegated admin if needed.
Preserve and review gateway, application, and Google audit logs.
WIF migration note
The credential interface is isolated so another provider can be added later, but this release implements and tests only a mounted service-account JSON key. Workload Identity Federation is not claimed as supported. For DWD, WIF is not necessarily a drop-in replacement for a JSON key: generating the DWD JWT assertion can require IAM Credentials signJwt permissions and explicit signing/exchange logic. Design and test that path before removing the JSON-key provider.
google-mcp
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceRead-only MCP server for Google Merchant Center, Google Search Console, Google Drive, Gmail, Calendar, and People API.MIT
- AlicenseAqualityAmaintenanceGoogle Workspace security-audit MCP server — read-only visibility into account locks, suspicious logins, and external file sharing, built on the Admin SDK Reports API (audit activities).14MIT
- AlicenseNot gradedqualityCmaintenanceAn admin-oriented Model Context Protocol server for Google Workspace that lets LLMs perform directory and user lifecycle operations with safety guardrails and audit logging.MIT
- AlicenseNot gradedqualityBmaintenanceSecure, read-only Google Search Console MCP server with exact property allowlists and a hardened TypeScript runtime.12MIT
Related MCP Connectors
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Federated commerce search across independent WooCommerce merchants. Keyless, read-only MCP server.
Identity resolution MCP server for phone/email lookups across 31+ services. Global + India coverage.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/AngelN-Halo/google-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server