Skip to main content
Glama
dipseth

google-workspace-unlimited

🚀 GoogleUnlimited Google Workspace Platform

docs pypi license privacy terms

google_workspace_fastmcp2 MCP server

GoogleUnlimited is a comprehensive MCP framework that provides seamless Google Workspace integration through an advanced middleware architecture. It enables AI assistants and MCP clients to interact with Gmail, Google Drive, Docs, Sheets, Slides, Calendar, Forms, Chat, Photos, and Contacts (People API) services using a unified, secure API.

What sets it apart:

  • Code Mode by default — instead of flooding your client with 90+ tool schemas, the server exposes 7 lightweight meta-tools; the AI discovers tools on demand and chains real API calls inside a single sandboxed execute block

  • 🚀 Zero-config startup — the server runs immediately with no .env file; OAuth happens lazily on first use

  • 🔧 Per-session tool control — URL-based service filtering and session-scoped enable/disable, so each connected client sees exactly the tools it needs

  • 🎨 Template & card DSL system — Jinja2 macros and a compact card notation turn raw API data into rich emails, dashboards, and Google Chat cards

  • 🧠 Semantic memory — every tool response is embedded into Qdrant, searchable later with natural language

📋 Table of Contents

Related MCP server: mcp-google-workspace

⚡ Quick Installation Instructions

What is GoogleUnlimited?

GoogleUnlimited provides AI assistants with access to Google Workspace services through the Model Context Protocol (MCP). It supports 92+ tools across 9 Google services, enabling seamless integration between AI workflows and Google Workspace applications with revolutionary performance improvements.

🛠️ Installation Methods

The fastest way to get started - install directly from PyPI:

{
  "mcpServers": {
    "google-workspace-unlimited": {
      "command": "uvx",
      "args": ["google-workspace-unlimited"],
      "disabled": false,
      "timeout": 300
    }
  }
}

That's it! The server runs in stdio mode by default, perfect for MCP clients like Claude Desktop, Cursor, Roo, etc. Code Mode is on out of the box, so your client sees 7 lean meta-tools instead of 90+ schemas.

Method 1b: Claude Code Plugin (server + skills)

Claude Code users can install the server and the skills that teach Claude its card/email DSL, code mode, and Qdrant search in two commands:

/plugin marketplace add dipseth/google_workspace_fastmcp2
/plugin install google-workspace-unlimited@riversunlimited

See plugins/google-workspace-unlimited for details.

Method 2: Clone and Development Setup

For development or customization:

  1. Clone and setup:

    git clone https://github.com/dipseth/google_workspace_fastmcp2.git
    cd google_workspace_fastmcp2
    uv sync
  2. Start the server:

    uv run python server.py

    The server starts immediately with zero configuration required. OAuth credentials are not needed at startup — authentication is handled lazily when you first interact with a Google service.

  3. Authenticate when ready:

    When you call any Google Workspace tool, the server will prompt you to authenticate via the start_google_auth tool. This opens a browser-based OAuth flow. Once completed, credentials are stored locally and reused across sessions.

    To pre-configure OAuth credentials (optional), create a .env file:

    cp .env.example .env

    Then add your Google Cloud Console credentials:

    # Option A: Client ID + Secret
    GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=your-client-secret
    
    # Option B: Downloaded JSON credentials file
    GOOGLE_CLIENT_SECRETS_FILE=credentials.json

    See the Google Cloud Console setup steps for creating OAuth credentials and enabling APIs.

📚 Configuration Resources:

📋 Environment Variables Reference

All environment variables are optional — the server starts with sensible defaults and no .env file required. OAuth credentials are only needed when initiating a new authentication flow via start_google_auth.

Google OAuth (needed for first-time authentication):

Variable

Default

Description

GOOGLE_CLIENT_ID

(empty)

OAuth 2.0 client ID from Google Cloud Console

GOOGLE_CLIENT_SECRET

(empty)

OAuth 2.0 client secret

GOOGLE_CLIENT_SECRETS_FILE

(empty)

Alternative: path to downloaded OAuth JSON file

OAUTH_REDIRECT_URI

http://localhost:8002/oauth2callback

Must match Google Console redirect URI

Provide either GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET or GOOGLE_CLIENT_SECRETS_FILE before your first OAuth flow. Once authenticated, credentials are stored locally and these variables are no longer needed.

Server:

Variable

Default

Description

SERVER_HOST

localhost

Server bind address

SERVER_PORT

8002

Server port

ENABLE_HTTPS

false

Enable HTTPS/SSL

SSL_CERT_FILE

-

Path to SSL certificate (required if HTTPS enabled)

SSL_KEY_FILE

-

Path to SSL private key (required if HTTPS enabled)

LOG_LEVEL

INFO

DEBUG, INFO, WARNING, ERROR

Security & Sessions:

Variable

Default

Description

CREDENTIAL_STORAGE_MODE

FILE_ENCRYPTED

FILE_ENCRYPTED, FILE_PLAINTEXT, MEMORY_ONLY

CREDENTIALS_DIR

./credentials

Directory for stored credentials

MCP_API_KEY

(empty)

Server API key — also used for crypto-bound credential encryption (HKDF-SHA256) and per-user key generation

SESSION_TIMEOUT_MINUTES

60

Session idle timeout

GMAIL_ALLOW_LIST

(empty)

Comma-separated trusted email addresses

Tool Management:

Variable

Default

Description

MINIMAL_TOOLS_STARTUP

true

Start with only 5 protected tools enabled

MINIMAL_STARTUP_SERVICES

(empty)

Comma-separated services to enable at startup (e.g., drive,gmail)

ENABLE_CODE_MODE

true

Code Mode (default) — replaces the full tool catalog with 7 meta-tools + sandboxed execute; set false for the classic catalog

ENABLE_SKILLS_PROVIDER

false

Enable FastMCP SkillsDirectoryProvider for dynamic skill generation

SKILLS_DIRECTORY

~/.claude/skills

Directory for generated skill documents

RESPONSE_LIMIT_MAX_SIZE

500000

Max tool response size in bytes (0 = disabled)

RESPONSE_LIMIT_TOOLS

(empty)

Comma-separated tool names to limit (empty = all)

Gmail Draft Preview Card:

Variable

Default

Description

DRAFT_PREVIEW_UI_GATING

true

Send a compact text summary instead of the card to clients showing no sign of MCP UI support

DRAFT_PREVIEW_UI_CLIENTS

claude-ai,claudeai,claude-desktop

clientInfo.name fragments treated as UI-capable even without the extension

DRAFT_PREVIEW_INLINE_IMAGES

true

Fetch remote email images and inline them as data: URIs so they render in the preview

Qdrant Vector Database:

Variable

Default

Description

QDRANT_URL

http://localhost:6333

Qdrant vector database URL

QDRANT_KEY

NONE

Qdrant API key (use NONE for no auth)

QDRANT_AUTO_LAUNCH

true

Auto-launch Qdrant via Docker if not reachable

QDRANT_DOCKER_IMAGE

qdrant/qdrant:latest

Docker image for auto-launch

QDRANT_DOCKER_CONTAINER_NAME

mcp-qdrant

Container name for auto-launched Qdrant

Other:

Variable

Default

Description

MCP_CHAT_WEBHOOK

(empty)

Default webhook URL for Google Chat card tools

FASTMCP_CLOUD

false

Enable cloud deployment mode (auto-switches to MEMORY_WITH_BACKUP storage)

🔗 Client Connections

GoogleUnlimited supports multiple connection methods. Here are the two most popular ways to get started:

🎯 Quick Setup Options

Option 1: Cursor IDE (STDIO - Community Verified ✅):

{
  "mcpServers": {
    "google-workspace": {
      "command": "uv",
      "args": [
        "--directory", "/path/to/google_workspace_fastmcp2",
        "run", "python", "server.py"
      ],
      "env": {
        "GOOGLE_CLIENT_SECRETS_FILE": "/path/to/client_secrets.json",
        "MCP_TRANSPORT": "stdio"
      }
    }
  }
}

Option 2: HTTP Streamable (VS Code Roo, Claude Code, Claude Desktop, etc.):

# Start server in HTTP mode
uv run python server.py --transport http --port 8002

Basic single-connection config:

{
  "google-workspace": {
    "type": "streamable-http",
    "url": "https://localhost:8002/mcp",
    "disabled": false
  }
}

Multi-connection setup — connect the same client (or multiple clients) to the same server with different tool sets using URL query parameters:

{
  "google-email": {
    "type": "streamable-http",
    "url": "https://localhost:8002/mcp?service=gmail"
  },
  "google-chat": {
    "type": "streamable-http",
    "url": "https://localhost:8002/mcp?service=chat"
  },
  "google-productivity": {
    "type": "streamable-http",
    "url": "https://localhost:8002/mcp?service=drive,docs,sheets,slides"
  }
}

Each connection gets its own isolated session with only the requested service tools enabled. You can also pin a session ID with ?uuid= to resume the same session state across reconnects:

{
  "google-workspace": {
    "type": "streamable-http",
    "url": "https://localhost:8002/mcp?uuid=my-workspace&service=gmail,drive,calendar"
  }
}

See URL-Based Service Filtering for the full list of query parameters.

🤖 Claude Code & Claude Desktop

Claude Code (CLI) — one command, using the published PyPI package:

# Local stdio (recommended): uvx fetches and runs the server on demand
claude mcp add google-workspace -- uvx google-workspace-unlimited

# Or connect to an already-running HTTP server
claude mcp add --transport http google-workspace https://localhost:8002/mcp

Claude Desktop (local dev path) — add to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "google-workspace-unlimited": {
      "command": "uvx",
      "args": ["google-workspace-unlimited"]
    }
  }
}

Claude Desktop (bridge to a server you already run) — recommended when you keep a local HTTP server up for development. Every command entry starts its own copy of the server, and Cowork / Code sessions start a second one on top of that; when startup is slow (Qdrant hydration on a cold cache runs ~12s) the client gives up first and reports Couldn't start this server … Request timed out. Bridging to the already-warm server connects in about a second instead:

{
  "mcpServers": {
    "google-workspace-local": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://localhost:8002/mcp"],
      "env": {
        "NODE_EXTRA_CA_CERTS": "/path/to/mkcert/rootCA.pem"
      }
    }
  }
}

mcp-remote registers itself through the server's OAuth 2.1 dynamic client registration, opens a browser once, and caches the token under ~/.mcp-auth — no API key in the config file, and rotating MCP_API_KEY does not break it. NODE_EXTRA_CA_CERTS is only needed when the server uses a self-signed certificate (Node will not trust mkcert's CA otherwise); drop it if you terminate TLS with a public certificate.

Claude.ai / Claude Desktop (hosted connector) — run the server behind a public HTTPS endpoint (e.g. a Cloudflare or ngrok tunnel), then add it under Settings → Connectors → Add custom connector with your https://your-domain/mcp URL. The server's OAuth 2.1 + PKCE flow handles authentication, including the https://claude.ai/api/mcp/auth_callback redirect. See the Claude.ai Integration Guide for the full walkthrough.

📚 Complete Connection Guide

For detailed setup instructions, troubleshooting, and configurations for all supported clients including:

  • Claude Code CLI (HTTP & STDIO)

  • Claude Desktop

  • VS Code / Roo / GitHub Copilot

  • Claude.ai with Cloudflare Tunnel

  • And more...

🔗 Complete Client Connection Guide - Comprehensive setup instructions, troubleshooting, and advanced configurations for all supported AI clients and development environments

⚡ Code Mode (Default)

Code Mode is GoogleUnlimited's flagship feature — and it's on by default. Instead of loading 90+ tool schemas upfront (expensive on tokens), your MCP client sees just 7 meta-tools. The AI discovers tools on demand, then chains any number of real API calls inside a single sandboxed Python execute block.

Meta-Tool

Purpose

tags

Browse tools by service category (Gmail, Drive, Calendar, etc.)

search

BM25-powered keyword search across tool names and descriptions

get_schema

Get full parameter schemas for selected tools

semantic_search

Natural-language search over previously stored tool responses (Qdrant-backed)

fetch_document

Retrieve a full stored response by point ID from search results

tool_activity

Summarize recent tool usage patterns and activity

execute

Run a sandboxed Python block that chains real tool calls via await call_tool(name, params)

Why it matters:

  • 💰 Massive token savings — 7 schemas instead of 90+, with full schemas fetched only for the tools actually used

  • 🔗 One round-trip instead of many — search → filter → act happens inside a single execute block, not a chain of client round-trips

  • 🧰 Batteries-included sandbox — 40+ built-in helpers (now(), days_ago(), to_json(), re_find(), gather_tools(), …) cover dates, JSON, URLs, regex, math, and batch calls without any imports

# One execute block: find a Drive file, then email its link
files = await call_tool("search_drive_files", {"query": "Q4 report"})
link = files["files"][0]["webViewLink"]
result = await call_tool("send_gmail_message", {
    "to": "manager@company.com",
    "subject": "Q4 Report",
    "body": "Here's the Q4 report: " + link,
})
return result

Prefer the classic catalog? Opt out and every tool is exposed directly to the client:

ENABLE_CODE_MODE=false   # expose the full 90+ tool catalog instead

Code Mode and the classic catalog are mutually exclusive — when Code Mode is active, direct tool calls are replaced by the search + execute pattern. Discovery tools always see the full catalog, regardless of session-level filtering.

🎯 Service Capabilities

GoogleUnlimited supports 10 Google Workspace services with 90+ specialized tools:

Service

Icon

Tools

Key Features

Documentation

Gmail

📧

14

Send, reply, labels, filters, search, allowlist, interactive draft preview card

api-reference/gmail/

Drive

📁

9

Upload, download, sharing, Office docs, file management

api-reference/drive/

Docs

📄

4

Create, edit, format, batch operations

api-reference/docs/

Sheets

📊

7

Read, write, formulas, formatting

api-reference/sheets/

Slides

🎯

5

Presentations, templates, export

api-reference/slides/

Calendar

📅

9

Events, scheduling, attendees, timezones

api-reference/calendar/

Forms

📝

8

Creation, responses, validation, publishing

api-reference/forms/

Chat

💬

24

Messaging, cards, spaces, webhooks, unified cards

api-reference/chat/

Photos

📷

12

Albums, upload, search, metadata, smart search

api-reference/photos/

People

👤

4

Name→email search (contacts + org directory), contact labels

people/

📚 API Documentation Resources:

🧠 Middleware Architecture

GoogleUnlimited uses a middleware architecture that provides seamless service integration, intelligent resource management, and powerful templating capabilities.

Middleware Architecture

🔧 Core Middleware Components

  • 🏷️ TagBasedResourceMiddleware: Intelligent resource discovery using URI patterns (service://gmail/messages, user://current/email)

  • 🧠 QdrantUnifiedMiddleware: AI-powered semantic search across all tool responses with vector embeddings

  • 🎨 TemplateMiddleware: Advanced Jinja2 template system for beautiful, structured output formatting

✨ Architecture Benefits

  • 🔄 Unified Resource Access: URI-based access to service data without API calls

  • 🧠 Semantic Intelligence: Natural language search across all stored responses

  • 🎨 Visual Excellence: Consistent, beautiful output formatting for optimal AI consumption

  • 💰 Token Efficiency: Template macros reduce token usage by 60-80% through structured data rendering

  • ⚡ Performance: 30x faster than traditional approaches through intelligent caching

📚 Middleware Documentation Resources:

🚀 Minimal Tools Startup

By default, GoogleUnlimited starts with only 5 protected tools enabled for optimal performance and security. This allows clients to enable only the tools they need.

Protected Tools (Always Available):

  • manage_tools - Enable/disable tools globally or per-session

  • manage_tools_by_analytics - Analytics-based tool management

  • health_check - Server health and configuration status

  • start_google_auth - Initiate OAuth authentication

  • check_drive_auth - Verify authentication status

Configuration:

# Default: Start with minimal tools (only 5 protected tools)
MINIMAL_TOOLS_STARTUP=true

# Optional: Pre-enable specific services at startup
MINIMAL_STARTUP_SERVICES=drive,gmail,calendar

# Disable minimal startup (enable all 92+ tools immediately)
MINIMAL_TOOLS_STARTUP=false

Enabling Tools at Runtime:

# Enable all tools globally
manage_tools(action="enable_all")

# Enable specific tools
manage_tools(action="enable", tool_names=["search_drive_files", "list_gmail_labels"])

# List all registered tools (shows enabled/disabled status)
manage_tools(action="list")

🔧 Session-Scoped Tool Management

GoogleUnlimited supports per-session tool enable/disable functionality, allowing different MCP clients to have different tool availability without affecting other connected clients.

Key Features:

  • Session Isolation: Disable tools for one client session without affecting others

  • Non-Invasive: Session-scoped operations never modify the global tool registry

  • Protected Tools: Core management tools (manage_tools, health_check, etc.) always remain available

  • Middleware-Based: Uses SessionToolFilteringMiddleware for protocol-level filtering

Usage Examples:

# Disable tools for this session only (other clients unaffected)
manage_tools(action="disable", tool_names=["send_gmail_message"], scope="session")

# Disable all except specific tools for this session
manage_tools(action="disable_all_except", tool_names=["search_drive_files", "list_events"], scope="session")

# Re-enable all tools for this session
manage_tools(action="enable_all", scope="session")

# Global operations (original behavior, affects all clients)
manage_tools(action="disable", tool_names=["send_gmail_message"], scope="global")

Response Structure:

{
  "success": true,
  "action": "disable_all_except",
  "scope": "session",
  "enabledCount": 94,
  "disabledCount": 0,
  "toolsAffected": ["tool1", "tool2", "..."],
  "sessionState": {
    "sessionId": "f725be09...",
    "sessionAvailable": true,
    "sessionDisabledTools": ["tool1", "tool2"],
    "sessionDisabledCount": 89
  },
  "message": "Kept 5 tools, disabled 89 tools for this session"
}

📚 Skills Provider

When enabled via ENABLE_SKILLS_PROVIDER=true, GoogleUnlimited generates skill documents from ModuleWrapper instances and serves them via FastMCP's SkillsDirectoryProvider. Skills provide structured knowledge that LLMs can reference for complex multi-step tasks.

Currently supported modules:

  • card_frameworkgchat-cards skill (Google Chat card DSL reference, component hierarchy, examples)

Configuration:

ENABLE_SKILLS_PROVIDER=true     # Enable skill generation
SKILLS_DIRECTORY=~/.claude/skills  # Output directory (default)

Skills are auto-regenerated on each startup and immediately available via the FastMCP skills system.

🖥️ Tool Management Dashboard

GoogleUnlimited includes a built-in Tool Management Dashboard served via the MCP Apps ui:// resource scheme. This provides a visual interface for monitoring and managing tool availability across sessions.

Tool Management Dashboard

Features:

  • Service-grouped tool view — tools organized by Google service (Gmail, Drive, Sheets, etc.) with counts

  • Session state visibility — see which tools are enabled, disabled, or session-disabled at a glance

  • Filter chips — quickly filter by service to focus on relevant tools

  • Live data — powered by DashboardCacheMiddleware which caches list-tool results for instant ui://data-dashboard resource access

The dashboard is automatically wired to all list tools via wire_dashboard_to_list_tools() — no per-tool configuration needed.

📊 Data Dashboards & Result Cards

Under Code Mode, a list tool called inside execute draws a data dashboard card: a searchable, sortable, paginated table. Gmail label colours render as the chips Gmail itself draws; nested values (filter criteria, actions) flatten to readable text.

Gmail Labels dashboard card — filter box, sortable columns, colour chips

The rows never enter the model's context. Hosts hand a tool result's structuredContent to the model as well as to the renderer, so a table embedded in the card cost ~80 tokens a row on every call — about 5k tokens for 65 labels. The card now ships as an empty shell (~375 tokens whatever the row count) and fetches its rows itself once drawn, through a UI-only dashboard_rows app tool keyed by an unguessable per-result token. The text content the model reads is unchanged.

Every execute block also ends in a result card showing the block's own return value — JSON printed one key per line, with a Copy button.

Execute result card — pretty-printed JSON with a Copy button

📧 Gmail Draft Preview Card

preview_gmail_draft returns a second MCP App: an interactive card showing a Gmail draft exactly as it will arrive, with Send, Save and Discard buttons and editable To/Cc/Bcc fields backed by contact autocomplete.

Gmail Draft Preview Card

# Draft and preview in one step — or pass a draft_id from draft_gmail_message
preview_gmail_draft(subject="Q3 numbers", body="...", to="team@example.com")

The card renders the real MJML/HTML body in a sandboxed iframe (no scripts — Gmail strips those too, so a script-free preview is both safer and more honest). Remote images are fetched and inlined as data: URIs, because hosts build the iframe's img-src from declared CSP domains and scheme-only grants are not honoured everywhere.

Payload discipline. A rendered view is not free: it travels in the tool result's structuredContent, and some hosts surface that to the model as well as to the renderer. Inlined images are therefore capped (150 KB per image, 500 KB per preview), and the server only builds the card for clients that show some sign of being able to draw it — either they advertised the MCP Apps UI extension, or their clientInfo.name matches DRAFT_PREVIEW_UI_CLIENTS. Everything else gets a compact text summary, skipping the image fetch and contact lookup entirely.

The server logs each client's identity once per session, so you can see which way a given host was routed:

[ui-gating] client=claude-ai version=2.1.0 advertises_extension=False allowlisted=True -> card

📚 Full details, including the Code Mode interaction and per-flag behaviour: docs/GMAIL_DRAFT_APP.md

🔗 URL-Based Service Filtering (HTTP Transport)

When using HTTP/SSE transport, you can filter tools by service directly via URL query parameters - no code required:

# Enable only Gmail tools
http://localhost:8002/mcp?service=gmail

# Enable Gmail + Drive + Calendar
http://localhost:8002/mcp?service=gmail,drive,calendar

# Resume a previous session
http://localhost:8002/mcp?uuid=your-session-id

# Resume session with specific services
http://localhost:8002/mcp?uuid=abc123&service=gmail,drive

# Disable minimal startup (enable all tools)
http://localhost:8002/mcp?minimal=false

Available URL Parameters:

Parameter

Example

Description

service or services

?service=gmail,drive

Comma-separated list of services to enable

uuid

?uuid=abc123

Resume a previous session by ID

minimal

?minimal=false

Override minimal startup mode

Available Services: gmail, drive, calendar, docs, sheets, slides, photos, chat, forms, people

📚 Session Tool Management Resources:

🎨 Template System

GoogleUnlimited features powerful Jinja2 template macros that transform raw Google Workspace data into visually stunning, AI-optimized formats.

🎯 Available Template Macros

Template File

Macro

Purpose

Key Features

email_card.j2

render_gmail_labels_chips()

Gmail label visualization

Interactive chips, unread counts, direct Gmail links

calendar_dashboard.j2

render_calendar_dashboard()

Calendar & events dashboard

Primary/shared calendars, upcoming events, dark theme

dynamic_macro.j2

render_calendar_events_dashboard()

Calendar events dashboard

Event cards, time/location details, clickable links, dark theme

document_templates.j2

generate_report_doc()

Professional reports

Metrics, tables, charts, company branding

colorfuL_email.j2

render_beautiful_email3()

Rich HTML emails

Multiple signatures, gradients, responsive design

💡 Template Macro Examples

Gmail Labels Visualization - Transform label lists into beautiful interactive chips:

{{ render_gmail_labels_chips( service://gmail/labels , 'Label summary for: ' + user://current/email ) }}

Calendar Dashboard - Create comprehensive calendar overviews:

{{ render_calendar_dashboard( service://calendar/calendars, service://calendar/events, 'My Calendar Overview' ) }}

Calendar Events Dashboard - Transform calendar events into beautiful, interactive event cards:

{{ render_calendar_events_dashboard( service://calendar/events , 'Upcoming Events for: ' + user://current/email.email ) }}

Calendar Events Dashboard Example

This macro creates a stunning dark-themed dashboard featuring:

  • 📅 Interactive Event Cards: Each event is rendered as a clickable card that opens in Google Calendar

  • 🕐 Smart Time Display: Automatically formats all-day events vs. timed events with timezone support

  • 📍 Location Integration: Displays meeting locations and virtual meeting links

  • 👥 Attendee Information: Shows attendee counts and participant details

  • Status Indicators: Color-coded status (confirmed, tentative, cancelled) with visual feedback

  • 📱 Responsive Design: Mobile-optimized layout with touch-friendly interactions

  • 🎨 Dark Theme Styling: Professional appearance with gradient backgrounds and hover effects

Professional Documents - Generate reports with metrics and charts:

{{ generate_report_doc(
    report_title='Q4 Performance Report',
    metrics=[{'value': '$1.2M', 'label': 'Revenue', 'change': 15}],
    company_name='Your Company'
) }}

🔍 Macro Discovery & Dynamic Creation

Explore all available macros using the template resource system:

# Access the template://macros resource to discover all available macros
macros = await access_resource("template://macros")
# Returns comprehensive macro information with usage examples

# Access specific macro details
macro_details = await access_resource("template://macros/render_gmail_labels_chips")

🎯 Dynamic Macro Creation

Create custom macros at runtime using the create_template_macro tool:

# Create a new macro dynamically
await create_template_macro(
    macro_name="render_task_status_badge",
    macro_content='''
    {% macro render_task_status_badge(status, size='small') %}
    {% if status == 'completed' %}
    <span class="status-badge status-completed {{ size }}">✅ Complete</span>
    {% elif status == 'in_progress' %}
    <span class="status-badge status-in-progress {{ size }}">🔄 In Progress</span>
    {% else %}
    <span class="status-badge status-pending {{ size }}">⏳ {{ status|title }}</span>
    {% endif %}
    {% endmacro %}
    ''',
    description="Renders visual status badges for task states with appropriate icons",
    usage_example="{{ render_task_status_badge('completed', 'large') }}",
    persist_to_file=True
)

# Immediately use the newly created macro
await send_gmail_message(
    html_body="Task Status: {{ render_task_status_badge('completed', 'large') }}"
)

DSL-powered macros — dynamic macros can also embed Google Chat card DSL notation to generate rich, structured cards. The DSL symbols define the card layout while Jinja2 handles dynamic content:

{# workspace_dashboard.j2 — a dynamic macro that outputs a Google Chat card #}
{% macro workspace_dashboard(user_email, stats=None, quick_actions=None) %}
{% set username = user_email.split('@')[0] if user_email else 'User' %}
{% set default_stats = stats or [
    {'label': 'Emails', 'value': '12 unread'},
    {'label': 'Calendar', 'value': '3 meetings today'},
    {'label': 'Tasks', 'value': '5 pending'}
] %}

§[δ×3, ℊ[ǵ×4], §[δ×2, Ƀ[ᵬ×3]]]

Welcome back, {{ username | title }}!

Your Workspace Overview:
{% for stat in default_stats %}
- {{ stat.label }}: {{ stat.value }}
{% endfor %}

Actions:
- Button: Open Gmail → https://mail.google.com
- Button: Open Calendar → https://calendar.google.com
- Button: Open Drive → https://drive.google.com
{% endmacro %}

The DSL line §[δ×3, ℊ[ǵ×4], §[δ×2, Ƀ[ᵬ×3]]] defines the card structure: a Section with 3 DecoratedText widgets, a Grid with 4 items, and a nested Section with 2 DecoratedText widgets and a ButtonList with 3 buttons. The Jinja2 template fills in the content dynamically — and because it's persisted to templates/dynamic/, it's immediately available to send_dynamic_card and other tools.

Key Features:

  • Immediate Availability: Macros are instantly available after creation

  • 🎯 Resource Integration: Automatically available via template://macros/macro_name

  • 💾 Optional Persistence: Save macros to disk for permanent availability

  • 🔄 Template Processing: Full Jinja2 syntax validation and error handling

  • 💬 DSL Integration: Macros can output card DSL notation for rich Google Chat cards

🚀 Real-World Usage

Templates can be directly used in tool calls for beautiful, structured output:

# Send a beautiful email with calendar dashboard
await send_gmail_message(
    to="manager@company.com",
    subject="Weekly Schedule Update",
    html_body="{{ render_calendar_events_dashboard( service://calendar/events, 'My upcoming events') }}",
    content_type="mixed"
)

# Generate and send a professional report
await create_doc(
    title="Q4 Performance Report",
    content="{{ generate_report_doc( report_title='Quarterly Results', company_name='GoogleUnlimited' ) }}"
)

📚 Template System Resources:

🗂️ Resource Discovery

GoogleUnlimited provides a powerful MCP resource system that enables lightning-fast data access without API calls through intelligent URI patterns.

Resource Discovery

🎯 Resource URI Patterns

Pattern

Purpose

Example

Returns

user://profile/{email}

User authentication status

user://profile/john@gmail.com

Profile + auth state

service://{service}/lists

Available service lists

service://gmail/lists

[filters, labels]

service://{service}/{list_type}

All items in list

service://gmail/labels

All Gmail labels

service://{service}/{list_type}/{id}

Specific item details

service://gmail/labels/INBOX

INBOX label details

recent://{service}

Recent items

recent://drive

Recent Drive files

qdrant://search/{query}

Semantic search

qdrant://search/gmail errors

Relevant responses

🏗️ Key Resource Files

⚡ Lightning-Fast Access

# Instant Gmail labels (no API call needed)
labels = await access_resource("service://gmail/labels")

# Current user info from session
user = await access_resource("user://current/email")

# Semantic search across all tool responses
results = await access_resource("qdrant://search/gmail errors today")

# Recent calendar events
events = await access_resource("recent://calendar")

📚 Resource System Documentation:

🧪 Testing Framework

GoogleUnlimited includes comprehensive testing with client tests that validate MCP usage exactly as an LLM would experience it, plus additional testing suites. 559 tests passing with 100% pass rate.

🎯 Client Testing Focus

Testing Framework

The client tests are the most important component - they provide deterministic testing of MCP operations using real resource integration and standardized patterns across all 92+ tools and 9 Google services. These tests validate both explicit email authentication and middleware injection patterns.

🚀 Quick Test Commands

# 🧪 Run all client tests (primary test suite)
uv run pytest tests/client/ -v

# 📧 Test specific service
uv run pytest tests/client/ -k "gmail" -v

# 🔐 Authentication required tests
uv run pytest tests/client/ -m "auth_required" -v

🔬 Real Resource ID Integration

The testing framework fetches real IDs from service resources for realistic testing:

# Available fixtures for real resource testing
real_gmail_message_id      # From service://gmail/messages
real_drive_document_id     # From service://drive/items
real_calendar_event_id     # From service://calendar/events
real_photos_album_id       # From service://photos/albums
real_forms_form_id         # From service://forms/forms
real_chat_space_id         # From service://chat/spaces

🔄 CI/CD Pipeline

Automated testing and publishing via GitHub Actions:

  • CI Workflow: Runs on every PR and push to main

    • Python 3.11 & 3.12 matrix testing

    • Linting with ruff check and formatting with ruff format

    • Full test suite execution

  • TestPyPI Publishing: Automated package publishing for testing

📚 Testing Resources:

🔒 Security & Authentication

GoogleUnlimited implements enterprise-grade security with OAuth 2.1 + PKCE, advanced session management, and comprehensive audit capabilities.

Security Architecture

🛡️ Authentication Flows

  1. 🌐 MCP Inspector OAuth: MCP Spec compliant with Dynamic Client Registration

  2. 🖥️ Direct Server OAuth: Web-based authentication for direct access

  3. 🔧 Development JWT: Testing mode with generated tokens

  4. 📁 Enhanced File Credentials: Persistent storage with encryption options

  5. 🔑 Custom OAuth Clients: Bring your own OAuth credentials with automatic fallback

  6. 🪪 Per-User API Keys: Individual keys generated on OAuth completion with credential isolation

✨ Security Features

  • 🔐 OAuth 2.1 + PKCE: Modern authentication with proof-of-key exchange (supports public clients)

  • 🔑 Per-User API Keys: Unique, revocable keys per user with hash-only storage and timing-safe lookup

  • 🛡️ Credential Isolation: Auth provenance-based access control prevents cross-user credential inheritance

  • 🔗 Account Linking: Bidirectional account linking for multi-account per-user key access

  • 🔒 Crypto-Bound Encryption: HKDF-SHA256 derived encryption keys bound to MCP_API_KEY

  • 🔒 Session Isolation: Multi-tenant support preventing data leaks

  • 🏷️ 27+ API Scopes: Granular permission management across all services

  • 📊 Audit Logging: Complete security event tracking with auth provenance

  • 🔐 AES-256 Encryption: Credential storage with legacy key migration support

  • 🔄 Three-Tier Fallback: Robust credential persistence across server restarts (State Map → UnifiedSession → Context Storage)

  • 🧹 Sensitive Data Stripping: Auth metadata removed from Qdrant embeddings before storage

⚙️ Security Configuration

# 🔒 Security settings in .env
CREDENTIAL_STORAGE_MODE=FILE_ENCRYPTED
SESSION_SECRET_KEY=your-secret-key
SESSION_TIMEOUT_MINUTES=30
ENABLE_AUDIT_LOGGING=true
GMAIL_ALLOW_LIST=trusted@example.com

📚 Security Documentation Resources:


🚀 Ready to revolutionize your Google Workspace integration?

📚 Documentation🔧 Configuration🎯 API Reference🧪 Testing

Available Tools

7 tools
executeA

Run sandboxed Python that calls this server's Google Workspace tools via await call_tool(tool_name, params), chaining calls in one block. Use when: you know which tools to call. To find tool names first use search or tags; for exact parameters use get_schema; to look up past results instead, use semantic_search. Behavior: each call_tool runs the real tool immediately — sends, edits, and deletes take effect; there is no dry-run. Use return to produce output; prefer returning the final answer from a single block. Only call_tool(tool_name: str, params: dict) -> Any is available in scope. Unknown tool names raise NotFoundError; disallowed syntax raises SandboxError.

SANDBOX RESTRICTIONS — these produce SandboxError, avoid them:

  • sorted_(items, key=lambda x: x['k']) → lambda args fail; use builtins like key=len or sort manually

  • import only covers a small stdlib subset (e.g. json); no third-party modules — prefer the built-in helpers listed below

Built-in helpers (import is not available — use these instead):

  • now(tz_offset=0) → current datetime string (UTC by default)

  • today(tz_offset=0) → current date 'YYYY-MM-DD' (UTC by default)

  • days_ago(n, tz_offset=0) → ISO datetime string N days ago

  • hours_ago(n, tz_offset=0) → ISO datetime string N hours ago

  • format_date(iso_str, fmt='%Y-%m-%d %H:%M') → formatted date

  • parse_date(iso_str) → normalized ISO datetime

  • timestamp() → current unix timestamp (int)

  • to_json(obj, indent=None) → JSON string

  • from_json(s) → parsed object

  • url_encode(s) → URL-encoded string

  • url_decode(s) → URL-decoded string

  • url_join(base, *parts) → joined URL path

  • query_string(params) → URL query string from dict

  • re_find(pattern, text) → list of matches

  • re_match(pattern, text) → bool

  • re_sub(pattern, repl, text) → substituted string

  • truncate(text, n=80) → truncated with '...'

  • dedent(text) → remove common leading whitespace

  • wrap_text(text, width=72) → word-wrap to width

  • pad_left(s, width, char=' ') → right-justify / zero-pad

  • pad_right(s, width, char=' ') → left-justify

  • join(items, sep=', ') → joined string

  • html_escape(s) → HTML-safe string

  • sqrt(n), ceil(n), floor(n) → math

  • round_(n, digits=2), abs_(), min_(), max_(), sum_() → math

  • sorted_(items, key=None, reverse=False) → sorted list

  • unique(items) → deduplicated list (preserves order)

  • flatten(lists) → flat list from nested lists

  • counter(items) → dict of {item: count}

  • chunk(items, size) → list of chunks

  • zip_(*iterables) → zipped as list of lists

  • dict_get(d, 'a.b.c', default=None) → nested dict access

  • md5(s), sha256(s) → hash hex digests

  • gather_tools(calls) → run multiple tool calls sequentially; calls is a list of [tool_name, params] pairs, returns list of results (assign to variable, then index: r = await gather_tools([...]); a, b = r[0], r[1])

  • sleep(seconds) → async sleep

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesPython async code to execute tool calls via call_tool(name, arguments)

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description is highly transparent about side effects and constraints: 'each call_tool runs the real tool immediately — sends, edits, and deletes take effect; there is no dry-run.' It also details sandbox restrictions, available built-in helpers, and error conditions (NotFoundError, SandboxError). With no annotations provided, this description fully compensates.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is long, it is well-structured with bold headers for use cases, sandbox restrictions, and helper functions. Every section earns its place given the complexity of a code execution environment. The most critical usage guidance is front-loaded, and the detailed reference lists are clearly organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, the lack of annotations, and the absence of an output schema, this description is exceptionally complete. It covers the execution model, side effects, sandbox limitations, available helpers, and error handling. It provides all the information an agent needs to use the tool correctly without additional lookups.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While the schema already describes the 'code' parameter, the description adds crucial semantic depth: the expected format (async Python code), the only available function in scope (call_tool), and the extensive list of built-in helpers. It also explains how to structure the code for chaining and return values. This goes well beyond the schema's basic description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Run sandboxed Python that calls this server's Google Workspace tools via await call_tool(tool_name, params), chaining calls in one block.' This is a specific verb+resource description that distinguishes the tool from siblings by emphasizing its role as a code-execution orchestrator.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes explicit usage guidance: 'Use when: you know which tools to call.' It also directs users to alternatives for different needs: 'To find tool names first use search or tags; for exact parameters use get_schema; to look up past results instead, use semantic_search.' This clearly delineates when to use this tool versus other siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fetch_documentA

Preview one stored tool response by its Qdrant point ID.

Use when: inspecting a hit returned by semantic_search. To find point IDs in the first place, use semantic_search; for the full untruncated content, call the fetch tool inside an execute block.

Behavior: read-only. Returns: tool name, service, timestamp, user, argument count, and the first 500 characters of stored content. Errors: 'Document not found' for unknown or expired point IDs.

ParametersJSON Schema
NameRequiredDescriptionDefault
point_idYesQdrant point ID (UUID) from a search result
user_google_emailNoUser's Google email (auto-injected by middleware)

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: 'read-only', the exact return payload (tool name, service, timestamp, user, argument count, first 500 characters), and error behavior ('Document not found' for unknown or expired IDs). This exceeds the burden placed on the description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured, using labeled sections (Use when, Behavior, Returns, Errors) that make it immediately scannable. Every sentence adds value, and there is no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Even though an output schema exists, the description provides a complete picture: what the tool does, when to use it, what it returns, and how it errors. This is especially strong given the lack of annotations, making the tool fully understandable for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including a clear definition for point_id and the auto-injected nature of user_google_email. The description adds no additional meaning beyond the schema, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Preview one stored tool response by its Qdrant point ID') with a specific verb, resource, and identifier. It also distinguishes itself from siblings by noting it works on results from semantic_search and defers full content to a different tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides when to use ('inspecting a hit returned by semantic_search'), how to find point IDs ('use semantic_search'), and when to use an alternative ('for the full untruncated content, call the fetch tool inside an execute block'). This gives clear and complete usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_schemaA

Get parameter schemas for named tools before calling them via execute. Use when: you already have tool names (from search or tags) and need exact parameters. Not for discovery — use search for that. Returns: per-tool parameter markdown ('detailed', default), names + descriptions ('brief'), or full JSON schemas ('full'); unknown names are reported under 'Tools not found'.

ParametersJSON Schema
NameRequiredDescriptionDefault
toolsYesList of tool names to get schemas for
detailNo'brief' for names and descriptions, 'detailed' for parameter schemas as markdown, 'full' for complete JSON schemasdetailed

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the output format options (brief/detailed/full), the default, and behavior for unknown names ('reported under 'Tools not found''). It does not explicitly state read-only safety, but this is strongly implied by the action of getting schemas.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise, front-loaded sentences. First sentence states purpose, second gives usage context, third lists output modes and edge-case behavior. Every sentence earns its place with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple (2 params, one enum) and the description covers use case, output formats, and not-found handling. An output schema exists, so return values don't need description. It is complete and self-sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds meaningful behavioral detail beyond the schema, particularly that unknown names are reported under a 'Tools not found' section and that 'detailed' yields markdown parameter schemas per tool. This enriches understanding of both parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Get parameter schemas for named tools before calling them via execute.' It uses a specific verb and resource, and distinguishes itself from siblings by explicitly saying 'Not for discovery — use search for that.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'when you already have tool names (from search or tags) and need exact parameters' and an explicit exclusion: 'Not for discovery — use search for that.' This fully clarifies context versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

tagsA

List this server's tool tags (service areas like gmail, drive, docs, photos) with tool counts. Use when: browsing what capability areas exist before a targeted lookup. For keyword lookup use search; for parameters of known tools use get_schema. Returns: '- tag (N tools)' lines at detail='brief' (default), or every tool listed under each tag at detail='full'.

ParametersJSON Schema
NameRequiredDescriptionDefault
detailNoLevel of detail: 'brief' for tag names and counts, 'full' for tools listed under each tagbrief

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the full burden. It discloses the default detail level and the exact output format for both 'brief' and 'full'. It doesn't explicitly state read-only behavior, but the listing nature is self-evident. Slight gap: no mention of permission or rate limits, yet not critical for a simple list.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise sentences with no filler. The first sentence states purpose, the second gives usage guidance, and the third explains return format. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter tool with an output schema, the description covers purpose, usage timing, alternatives, default behavior, and output format for both detail levels. It is complete and fully contextualized relative to its complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and the schema already documents the 'detail' parameter with enum, default, and a clear description. The tool description adds the specific output format ('- tag (N tools)' lines), which is a small but useful enhancement beyond the schema. Baseline 3, slightly above.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with a specific verb+resource: 'List this server's tool tags' and clarifies with examples of service areas. It clearly distinguishes from siblings by mentioning 'search' for keyword lookup and 'get_schema' for parameters.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit use case: 'Use when: browsing what capability areas exist before a targeted lookup.' It names alternatives: 'For keyword lookup use search; for parameters of known tools use get_schema.' This is clear when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

tool_activityA

Show usage analytics for this server's tools: call counts, error rates, last-used times.

Use when: answering 'what has been used or failing lately'. To read an individual response, pass a sample point ID to fetch_document; to discover tools to call, use search instead.

Behavior: read-only aggregation over the Qdrant response store. Returns: a text dashboard grouped by tool_name or user_email, with sample point IDs per group. Errors: 'Analytics failed' when the response store is unreachable.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum groups to show
group_byNoGroup results by 'tool_name' or 'user_email'tool_name
user_google_emailNoUser's Google email (auto-injected by middleware)

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It explicitly states 'read-only aggregation' which reassures the agent that this is a safe, non-mutating operation. It also discloses the error condition ('Analytics failed' when the response store is unreachable) and the underlying data store (Qdrant response store), which adds meaningful context beyond the basic function.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is highly efficient and well-structured, using labeled sections ('Use when', 'Behavior', 'Returns', 'Errors') to front-load key information. Every sentence contributes value, with no repetition of schema details or fluff. The entire text is compact while covering purpose, usage, behavior, return format, and error handling.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema (which lowers the burden for explaining return values), the description still describes the return format: 'a text dashboard grouped by tool_name or user_email, with sample point IDs per group.' It also covers the error case and the data source. For a low-complexity read-only analytics tool, this is complete and self-contained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema covers all three parameters with descriptions (limit, group_by, user_google_email), achieving 100% coverage. The description adds no new parametric details beyond what the schema already provides, such as mentioning group_by values ('tool_name' or 'user_email') which are already in the schema. Baseline of 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Show usage analytics for this server's tools: call counts, error rates, last-used times.' This clearly states what the tool does and its scope. It distinguishes itself from sibling tools like fetch_document (reading a single document) and search (discovering tools) by focusing on aggregate usage analytics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides an explicit 'Use when' clause: 'answering what has been used or failing lately.' It also names alternatives: 'To read an individual response, pass a sample point ID to fetch_document; to discover tools to call, use search instead.' This gives clear context for when to use this tool versus its siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.5/5.0
Disambiguation5/5

Each tool has a clearly distinct role: tags for browsing categories, search for keyword discovery, get_schema for parameters, execute for running tools, semantic_search for past results, fetch_document for previewing stored content, and tool_activity for analytics. No two tools have overlapping purposes.

Naming Consistency3/5

Names mix verbs (execute, search), nouns (tags, tool_activity), and verb-noun compounds (get_schema, fetch_document). There is no consistent verb_noun pattern across all tools, though they are all lowercase with underscores for multi-word names, making them still readable.

Tool Count5/5

Seven tools is well-suited for a meta-server that wraps Google Workspace access. They cover discovery, schema lookup, execution, history search, document preview, and analytics without being excessive or sparse.

Completeness4/5

The set provides a complete workflow from discovering tools (search/tags) to understanding parameters (get_schema) to executing (execute) to auditing (semantic_search, fetch_document, tool_activity). The only minor gap is that actual Google Workspace tools are not exposed as first-class tool entries, but they are accessible via execute, so the surface is effectively complete.

Maintenance

ActivityActive
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

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/dipseth/google_workspace_fastmcp2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server