
Connect MCP Clients, AI Assistants and more to Google Workspace services through the Model Context Protocol
See it in action:
google-calendar-mcp-demo.mov
The Google Workspace MCP Server integrates Google Workspace services (Calendar, Drive, Gmail, and Docs) with AI assistants and other applications using the Model Context Protocol (MCP). This allows AI systems to access and interact with user data from Google Workspace applications securely and efficiently.
- π OAuth 2.0 Authentication: Securely connects to Google APIs using user-authorized credentials with automatic token refresh and centralized authentication flow
- π Google Calendar Integration: Full calendar management - list calendars, fetch events, create/modify/delete events with support for all-day and timed events
- π Google Drive Integration: Search files, list folder contents, read file content, and create new files. Supports extraction and retrieval of .docx, .xlsx and other Microsoft Office formats natively!
- π§ Gmail Integration: Complete email management - search messages, retrieve content, send emails, and create drafts with full support for all query syntax
- π Google Docs Integration: Search for documents, read document content, list documents in folders, and create new documents right from your chat!
- π Multiple Transport Options: Streamable HTTP + SSE fallback
- π
mcpo
Compatibility: Easily expose the server as an OpenAPI endpoint for integration with tools like Open WebUI - π§© Extensible Design: Simple structure for adding support for more Google Workspace APIs and tools
- π Integrated OAuth Callback: Handles the OAuth redirect directly within the server on port 8000
- β‘ Thread-Safe Session Management: Robust session handling with thread-safe architecture for improved reliability
- Python 3.11+
- uv package installer (or pip)
- Google Cloud Project with OAuth 2.0 credentials enabled for required APIs (Calendar, Drive, Gmail, Docs)
To install Google Workspace Integration Server for Claude Desktop automatically via Smithery:
npx -y @smithery/cli install @taylorwilsdon/google_workspace_mcp --client claude
# Clone the repository (replace with your fork URL if different)
git clone https://github.com/taylorwilsdon/google_workspace_mcp.git
cd google_workspace_mcp
# Create a virtual environment and install dependencies
uv venv
source .venv/bin/activate # On Windows use `.venv\Scripts\activate`
uv pip install -e .
- Create OAuth 2.0 Credentials (Desktop application type) in the Google Cloud Console.
- Enable the Google Calendar API, Google Drive API, Gmail API, and Google Docs API for your project.
- Download the OAuth client credentials as
client_secret.json
and place it in the project's root directory. - Add the following redirect URI to your OAuth client configuration in the Google Cloud Console. Note that
http://localhost:8000
is the default base URI and port, which can be customized via environment variables (WORKSPACE_MCP_BASE_URI
andWORKSPACE_MCP_PORT
). If you change these, you must update the redirect URI in the Google Cloud Console accordingly.http://localhost:8000/oauth2callback
β οΈ Important: Ensureclient_secret.json
is added to your.gitignore
file and never committed to version control.
The server's base URL and port can be customized using environment variables:
WORKSPACE_MCP_BASE_URI
: Sets the base URI for the server (default:http://localhost
). This affects theserver_url
used for Gemini native function calling and theOAUTH_REDIRECT_URI
.WORKSPACE_MCP_PORT
: Sets the port the server listens on (default:8000
). This affects theserver_url
,port
, andOAUTH_REDIRECT_URI
.
Example usage:
export WORKSPACE_MCP_BASE_URI="https://my-custom-domain.com"
export WORKSPACE_MCP_PORT="9000"
uv run main.py
The server uses HTTP for localhost OAuth callbacks during development. Set this environment variable before running the server:
# Allow HTTP for localhost OAuth callbacks (development only!)
export OAUTHLIB_INSECURE_TRANSPORT=1
Without this, you might encounter an "OAuth 2 MUST utilize HTTPS" error during the authentication flow.
Choose one of the following methods to run the server:
HTTP Server Mode
python main.py
# or using uv
uv run main.py
Runs the server with an HTTP transport layer on port 8000.
Single-User Mode
Multi-user MCP is kind of a mess, so right now now everything runs best as 1:1 mapping between client snd server. That will change as soon as Claude can permform OAuth 2.1 flows, so this MCP was built eith a flag for simplified single-user environments. You can run the server in single-user mode, which bypasses session-to-OAuth mapping and uses any available credentials from the .credentials
directory:
python main.py --single-user
# or using uv
uv run main.py --single-user
In single-user mode:
- The server automatically finds and uses any valid credentials in the
.credentials
directory - No session mapping is required - the server uses the first valid credential file found
- Useful for development, testing, or single-user deployments
- Still requires initial OAuth authentication to create credential files
This mode is particularly helpful when you don't need multi-user session management and want simplified credential handling.
Using Docker
You can build and run the server using the provided Dockerfile
.
# Build the Docker image
docker build -t google-workspace-mcp .
# Run the Docker container
# The -p flag maps the container port 8000 to the host port 8000
# The -v flag mounts the current directory to /app inside the container
# This is useful for development to pick up code changes without rebuilding
docker run -p 8000:8000 -v $(pwd):/app google-workspace-mcp
The smithery.yaml
file is configured to start the server correctly within the Docker container.
The default ports are 8000
, but can be changed via the WORKSPACE_MCP_PORT
environment variable.
Service | Default Port | Description |
---|---|---|
OAuth Callback | 8000 |
Handled internally by the server via the /oauth2callback route |
HTTP Mode Server | 8000 |
Default when using HTTP transport |
The server supports multiple connection methods:
Claude Desktop:
Can run anywhere and be used via
mcp-remote
or invoked locally withuv run main.py
and usingmcp-remote
with localhost. Claude Desktop lacks native support for streamable HTTP or SSE.
config.json:
{
"mcpServers": {
"Google workspace": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/mcpβ
]
}
}
}
- Install
mcpo
:uv pip install mcpo
orpip install mcpo
- Create a
config.json
(see Integration with Open WebUI) - Run
mcpo
pointing to your config:uvx mcpo --config config.json --port 8001
- The MCP server API will be available at:
http://localhost:8001/google_workspace
(or the name defined inconfig.json
) - OpenAPI documentation (Swagger UI) available at:
http://localhost:8001/google_workspace/docs
With startup command (for single-mcp mcpo usage):
- Install
mcpo
:uv pip install mcpo
orpip install mcpo
- Start with
uvx mcpo --port 8001 --api-key "top-secret" --server-type "streamablehttp" -- http://localhost:8000/mcp
- The MCP server API will be available at:
http://localhost:8001/openapi.json
(or the name defined inconfig.json
) - OpenAPI documentation (Swagger UI) available at:
http://localhost:8001/docs
- Start the server in HTTP mode (see Start the Server)
- Send MCP JSON requests directly to
http://localhost:8000
- Useful for testing with tools like
curl
or custom HTTP clients - Can be used to serve Claude Desktop & other MCP clients yet to integrate the new Streamable HTTP transport via mcp-remote:
- You can also serve in SSE fallback mode if preferred.
To use this server as a tool provider within Open WebUI:
-
Create
mcpo
Configuration: Create a file namedconfig.json
with the following structure to have mcpo make the streamable HTTP endpoint available as an OpenAPI spec tool.{ "mcpServers": { "google_workspace": { "type": "streamablehttp", "url": "http://localhost:8000/mcp" } } }
-
Start the
mcpo
Server:mcpo --port 8001 --config config.json --api-key "your-optional-secret-key"
This command starts the
mcpo
proxy, serving your active (assuming port 8000) Google Workspace MCP on port 8001. -
Configure Open WebUI:
- Navigate to your Open WebUI settings
- Go to "Connections" -> "Tools"
- Click "Add Tool"
- Enter the Server URL:
http://localhost:8001/google_workspace
(matching themcpo
base URL and server name fromconfig.json
) - If you used an
--api-key
withmcpo
, enter it as the API Key - Save the configuration
- The Google Workspace tools should now be available when interacting with models in Open WebUI
When a tool requiring Google API access is called:
- If
user_google_email
is provided to the tool and credentials are missing/invalid: The server automatically initiates the OAuth 2.0 flow. An authorization URL will be returned in the MCP response (or printed to the console). - If
user_google_email
is NOT provided and credentials are missing/invalid: The tool will return an error message guiding the LLM to use the centralizedstart_google_auth
tool. The LLM should then callstart_google_auth
with the user's email and the appropriateservice_name
(e.g., "Google Calendar", "Google Docs", "Gmail", "Google Drive"). This will also return an authorization URL.
Steps for the User (once an authorization URL is obtained):
- Open the provided authorization URL in a web browser.
- Log in to the Google account and grant the requested permissions for the specified service.
- After authorization, Google will redirect the browser to
http://localhost:8000/oauth2callback
(or your configured redirect URI). - The MCP server handles this callback, exchanges the authorization code for tokens, and securely stores the credentials.
- The LLM can then retry the original request. Subsequent calls for the same user and service should work without re-authentication until the refresh token expires or is revoked.
Note: The first use of any tool for a specific Google service may trigger the OAuth authentication flow if valid credentials are not already stored and
user_google_email
is provided to the tool. If authentication is required anduser_google_email
is not provided to the tool, the LLM should use the centralizedstart_google_auth
tool (defined incore/server.py
) with the user's email and the appropriateservice_name
.
Source: gcalendar/calendar_tools.py
Tool | Description | Parameters |
---|---|---|
start_google_auth |
(Centralized in core/server.py ) Initiates the OAuth 2.0 authentication flow for a specific Google account and service. Use this when no valid credentials are available or if a tool fails due to missing authentication and an email was not provided to it. |
β’ user_google_email (required): The user's Google email addressβ’ service_name (required): The Google service name (e.g., "Google Calendar", "Google Docs", "Gmail", "Google Drive") |
list_calendars |
Lists all calendars accessible to the authenticated user. | β’ user_google_email (optional): Used if session is not authenticatedβ’ mcp_session_id (injected automatically) |
get_events |
Retrieves upcoming events from a specified calendar within a time range. | β’ calendar_id (optional): Calendar ID (default: primary )β’ time_min (optional): Start time (RFC3339 or YYYY-MM-DD )β’ time_max (optional): End time (RFC3339 or YYYY-MM-DD )β’ max_results (optional): Max number of events (default: 25)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
create_event |
Creates a new calendar event. Supports all-day and timed events. | β’ summary (required): Event titleβ’ start_time (required): Start time (RFC3339 or YYYY-MM-DD )β’ end_time (required): End time (RFC3339 or YYYY-MM-DD )β’ calendar_id (optional): Calendar ID (default: primary )β’ description , location , attendees , timezone (optional)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
modify_event |
Updates an existing event by ID. Only provided fields will be modified. | β’ event_id (required): ID of the event to modifyβ’ calendar_id (optional): Calendar ID (default: primary )β’ summary , start_time , end_time , description , location , attendees , timezone (optional)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
delete_event |
Deletes an event by ID. | β’ event_id (required): ID of the event to deleteβ’ calendar_id (optional): Calendar ID (default: primary )β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
βΉοΈ All Calendar tools support authentication via the current MCP session (
mcp_session_id
) or fallback touser_google_email
. If neither is available and authentication is required, the tool will return an error prompting the LLM to use the centralizedstart_google_auth
tool with the user's email andservice_name="Google Calendar"
.
π Date/Time Parameters: Tools accept both full RFC3339 timestamps (e.g., 2024-05-12T10:00:00Z) and simple dates (e.g., 2024-05-12). The server automatically formats them as needed.
Source: gdrive/drive_tools.py
Tool | Description | Parameters |
---|---|---|
search_drive_files |
Searches for files and folders across the user's Drive | β’ query (required): Search query string (e.g., name contains 'report' )β’ max_results (optional): Maximum number of files to return |
get_drive_file_content |
Retrieves the content of a specific file | β’ file_id (required): The ID of the fileβ’ mime_type (optional): Specify the desired export format |
list_drive_items |
Lists files and folders within a specific folder or the root | β’ folder_id (optional): The ID of the folder to list (defaults to root)β’ max_results (optional): Maximum number of items to return |
create_drive_file |
Creates a new file in Google Drive | β’ name (required): The desired name for the new fileβ’ content (required): The text content to write into the fileβ’ folder_id (optional): The ID of the parent folderβ’ mime_type (optional): The MIME type of the file (defaults to text/plain ) |
Query Syntax: For Google Drive search queries, see Drive Search Query Syntax
Source: gmail/gmail_tools.py
Tool | Description | Parameters |
---|---|---|
search_gmail_messages |
Search email messages using standard Gmail search operators (from, subject, etc). | β’ query (required): Search string (e.g., "from:foo subject:bar is:unread" )β’ user_google_email (optional)β’ page_size (optional, default: 10)β’ mcp_session_id (injected automatically) |
get_gmail_message_content |
Get subject, sender, and plain text body of an email by message ID. | β’ message_id (required)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
send_gmail_message |
Send a plain text email using the user's Gmail account. | β’ to (required): Recipient email addressβ’ subject (required)β’ body (required)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
draft_gmail_message |
Create a draft email in the user's Gmail account. | β’ subject (required): Email subjectβ’ body (required): Email body (plain text)β’ to (optional): Recipient email addressβ’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
Query Syntax: For Gmail search queries, see Gmail Search Query Syntax
Source: gdocs/docs_tools.py
Tool | Description | Parameters |
---|---|---|
search_docs |
Search for Google Docs by name (using Drive API). | β’ query (required): Text to search for in Doc namesβ’ user_google_email (optional)β’ page_size (optional, default: 10)β’ mcp_session_id (injected automatically) |
get_doc_content |
Retrieve the plain text content of a Google Doc by its document ID. | β’ document_id (required)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
list_docs_in_folder |
List all Google Docs inside a given Drive folder (by folder ID, default = root ). |
β’ folder_id (optional, default: 'root' )β’ user_google_email (optional)β’ page_size (optional, default: 100)β’ mcp_session_id (injected automatically) |
create_doc |
Create a new Google Doc, optionally with initial content. | β’ title (required): Name for the docβ’ content (optional, default: empty)β’ user_google_email (optional)β’ mcp_session_id (injected automatically) |
google_workspace_mcp/
βββ .venv/ # Virtual environment (created by uv)
βββ auth/ # OAuth handling logic (google_auth.py, oauth_manager.py)
βββ core/ # Core MCP server logic (server.py)
βββ gcalendar/ # Google Calendar tools (calendar_tools.py)
βββ gdocs/ # Google Docs tools (docs_tools.py)
βββ gdrive/ # Google Drive tools (drive_tools.py)
βββ gmail/ # Gmail tools (gmail_tools.py)
βββ .gitignore # Git ignore file
βββ client_secret.json # Google OAuth Credentials (DO NOT COMMIT)
βββ config.json # Example mcpo configuration
βββ main.py # Main server entry point (imports tools)
βββ mcp_server_debug.log # Log file for debugging
βββ pyproject.toml # Project metadata and dependencies (for uv/pip)
βββ README.md # This file
βββ uv.lock # uv lock file
The server cleverly handles the OAuth 2.0 redirect URI (/oauth2callback
) without needing a separate web server framework:
- It utilizes the built-in HTTP server capabilities of the underlying MCP library when run in HTTP mode or via
mcpo
- A custom MCP route is registered specifically for
/oauth2callback
on port8000
- When Google redirects the user back after authorization, the MCP server intercepts the request on this route
- The
auth
module extracts the authorization code and completes the token exchange - This requires
OAUTHLIB_INSECURE_TRANSPORT=1
to be set when running locally, as the callback useshttp://localhost
Log File
Check mcp_server_debug.log
for detailed logs, including authentication steps and API calls. Enable debug logging if needed.
OAuth Issues
- Verify
client_secret.json
is correct and present - Ensure the correct redirect URI (
http://localhost:8000/oauth2callback
) is configured in Google Cloud Console - Confirm the necessary APIs (Calendar, Drive, Gmail) are enabled in your Google Cloud project
- Check that
OAUTHLIB_INSECURE_TRANSPORT=1
is set in the environment where the server process runs - Look for specific error messages during the browser-based OAuth flow
Tool Errors
Check the server logs for tracebacks or error messages returned from the Google APIs.
- Choose or create the appropriate module (e.g.,
gdocs/gdocs_tools.py
) - Import necessary libraries (Google API client library, etc.)
- Define an
async
function for your tool logic. Use type hints for parameters - Decorate the function with
@server.tool("your_tool_name")
- Inside the function, get authenticated credentials:
from auth.google_auth import get_credentials, CONFIG_CLIENT_SECRETS_PATH
# ...
credentials = await asyncio.to_thread(
get_credentials,
user_google_email=your_user_email_variable, # Optional, can be None if session_id is primary
required_scopes=YOUR_SPECIFIC_SCOPES_LIST, # e.g., [CALENDAR_READONLY_SCOPE]
client_secrets_path=CONFIG_CLIENT_SECRETS_PATH,
session_id=your_mcp_session_id_variable # Usually injected via Header
)
if not credentials or not credentials.valid:
# Handle missing/invalid credentials, possibly by calling start_auth_flow
# from auth.google_auth (which is what service-specific start_auth tools do)
pass
- Build the Google API service client:
service = build('drive', 'v3', credentials=credentials)
- Implement the logic to call the Google API
- Handle potential errors gracefully
- Return the results as a JSON-serializable dictionary or list
- Import the tool function in
main.py
so it gets registered with the server - Define necessary service-specific scope constants in your tool's module
- Update
pyproject.toml
if new dependencies are required
Scope Management: The global
SCOPES
list inconfig/google_config.py
is used for the initial OAuth consent screen. Individual tools should request the minimalrequired_scopes
they need when callingget_credentials
.
-
client_secret.json
: This file contains sensitive credentials. NEVER commit it to version control. Ensure it's listed in your.gitignore
file. Store it securely. -
User Tokens: Authenticated user credentials (refresh tokens) are stored locally in files like
credentials-<user_id_hash>.json
. Protect these files as they grant access to the user's Google account data. Ensure they are also in.gitignore
. -
OAuth Callback Security: The use of
http://localhost
for the OAuth callback is standard for installed applications during development but requiresOAUTHLIB_INSECURE_TRANSPORT=1
. For production deployments outside of localhost, you MUST use HTTPS for the callback URI and configure it accordingly in Google Cloud Console. -
mcpo
Security: If usingmcpo
to expose the server over the network, consider:- Using the
--api-key
option for basic authentication - Running
mcpo
behind a reverse proxy (like Nginx or Caddy) to handle HTTPS termination, proper logging, and more robust authentication - Binding
mcpo
only to trusted network interfaces if exposing it beyond localhost
- Using the
-
Scope Management: The server requests specific OAuth scopes (permissions) for Calendar, Drive, and Gmail. Users grant access based on these scopes during the initial authentication. Do not request broader scopes than necessary for the implemented tools.



This project is licensed under the MIT License - see the LICENSE
file for details.